diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2affc0..b8d7613 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: run: | cmake -B build \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)" \ -DPRIVACY_PASS_BUILD_TESTS=ON \ -DPRIVACY_PASS_BUILD_BENCHMARKS=ON \ -DPRIVACY_PASS_BUILD_MOQ=ON diff --git a/.gitignore b/.gitignore index b8eb424..974de4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Build directories build/ +build-examples/ cmake-build-*/ # IDE diff --git a/CMakeLists.txt b/CMakeLists.txt index d4fbd83..5885f0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ function(privacy_pass_set_warnings target) endfunction() # Dependencies -find_package(OpenSSL REQUIRED) +find_package(OpenSSL 3.0 REQUIRED) include(FetchContent) @@ -58,6 +58,14 @@ if(PRIVACY_PASS_BUILD_TESTS) GIT_TAG v2.4.12 ) FetchContent_MakeAvailable(doctest) + + set(JSON_BuildTests OFF CACHE INTERNAL "") + FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.11.3 + ) + FetchContent_MakeAvailable(nlohmann_json) endif() # Fetch Google Benchmark if building benchmarks @@ -244,6 +252,10 @@ if(PRIVACY_PASS_BUILD_TESTS) target_link_libraries(privacy_pass_tests PRIVATE privacy_pass doctest::doctest + nlohmann_json::nlohmann_json + ) + target_compile_definitions(privacy_pass_tests PRIVATE + PRIVACY_PASS_TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/test_data" ) if(PRIVACY_PASS_BUILD_MOQ) target_link_libraries(privacy_pass_tests PRIVATE privacy_pass_moq) diff --git a/README.md b/README.md index e4464d9..774dab0 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ | macOS | [![macOS](https://github.com/Quicr/privacypass-cpp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Quicr/privacypass-cpp/actions/workflows/ci.yml?query=branch%3Amain) | | Windows | [![Windows](https://github.com/Quicr/privacypass-cpp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Quicr/privacypass-cpp/actions/workflows/ci.yml?query=branch%3Amain) | -C++ implementation of the Privacy Pass protocol (RFC 9576-9578) with optional MOQ extension support. +C++ implementation of the Privacy Pass protocol ([RFC9576](https://datatracker.ietf.org/doc/html/rfc9576), [RFC9577](https://datatracker.ietf.org/doc/html/rfc9577), [RFC9578](https://datatracker.ietf.org/doc/html/rfc9578)) with optional MOQ extension ([draft-ietf-moq-privacy-pass-auth-02](https://datatracker.ietf.org/doc/html/draft-ietf-moq-privacy-pass-auth-02)) support. ## Features - Blind RSA (Token Type 0x0002) and VOPRF P-384 (Token Type 0x0001) - Token challenges, requests, responses, and redemption -- Optional MOQ extension (draft-ietf-moq-privacy-pass-auth) +- Optional MOQ extension ([draft-ietf-moq-privacy-pass-auth-02](https://datatracker.ietf.org/doc/html/draft-ietf-moq-privacy-pass-auth-02)) - Modern C++23 with `std::expected` error handling ## Building diff --git a/examples/moq_client.cpp b/examples/moq_client.cpp index 6e204dc..75c6035 100644 --- a/examples/moq_client.cpp +++ b/examples/moq_client.cpp @@ -29,7 +29,7 @@ void print_hex(const std::string& label, ByteView data, size_t max_bytes = 16) { // Example MOQ Client built on TokenProvider class MoqClient { public: - MoqClient() : provider_(TokenProviderConfig{.max_cached_tokens = 10}) {} + MoqClient() : provider_(TokenProviderConfig{.max_cached_tokens = 10, .origin_name = "origin.example.com"}) {} void add_issuer(const std::string& name, const PublicKey& key) { provider_.add_issuer_key(name, key); diff --git a/include/privacy_pass/core/serialization.hpp b/include/privacy_pass/core/serialization.hpp index 28b928d..1107dfc 100644 --- a/include/privacy_pass/core/serialization.hpp +++ b/include/privacy_pass/core/serialization.hpp @@ -298,6 +298,7 @@ class ByteWriter { namespace base64url { [[nodiscard]] std::string encode(ByteView data); +[[nodiscard]] std::string encode_padded(ByteView data); [[nodiscard]] Result decode(std::string_view encoded); } // namespace base64url diff --git a/include/privacy_pass/core/token_provider.hpp b/include/privacy_pass/core/token_provider.hpp index 83c0739..3676769 100644 --- a/include/privacy_pass/core/token_provider.hpp +++ b/include/privacy_pass/core/token_provider.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace privacy_pass { @@ -28,6 +29,7 @@ struct TokenProviderConfig { size_t max_cached_tokens{100}; std::chrono::seconds token_prefetch_threshold{60}; bool allow_token_reuse{false}; // For testing only + std::string origin_name; }; // Callback for async token fetching diff --git a/include/privacy_pass/core/token_request.hpp b/include/privacy_pass/core/token_request.hpp index 93987c2..70c1010 100644 --- a/include/privacy_pass/core/token_request.hpp +++ b/include/privacy_pass/core/token_request.hpp @@ -14,7 +14,7 @@ namespace privacy_pass { struct TokenRequest { TokenType token_type; uint8_t truncated_token_key_id; // LSB of full key ID - Bytes blinded_msg; // Nk bytes for RSA, 97 bytes for VOPRF P-384 + Bytes blinded_msg; // Nk bytes for RSA, 49 bytes for VOPRF P-384 // Create a token request static TokenRequest create( diff --git a/include/privacy_pass/core/token_response.hpp b/include/privacy_pass/core/token_response.hpp index 98ec7f0..6f0d4c5 100644 --- a/include/privacy_pass/core/token_response.hpp +++ b/include/privacy_pass/core/token_response.hpp @@ -22,7 +22,7 @@ struct BlindRsaTokenResponse { // VOPRF token response (P-384) struct VoprfTokenResponse { - Bytes evaluate_msg; // Ne = 97 bytes (compressed P-384 point) + Bytes evaluate_msg; // Ne = 49 bytes (compressed P-384 point) Bytes evaluate_proof; // 2*Ns = 96 bytes (DLEQ proof) [[nodiscard]] Result serialize() const; diff --git a/include/privacy_pass/core/types.hpp b/include/privacy_pass/core/types.hpp index 6e35684..9a7c57e 100644 --- a/include/privacy_pass/core/types.hpp +++ b/include/privacy_pass/core/types.hpp @@ -100,7 +100,7 @@ struct TokenTypeInfo { static constexpr TokenTypeInfo for_type(TokenType t) noexcept { switch (t) { case TokenType::VOPRF_P384_SHA384: - return {t, 48, 32, 97, false}; + return {t, 48, 32, 49, false}; case TokenType::BLIND_RSA: return {t, 256, 32, 256, true}; case TokenType::VOPRF_RISTRETTO255_SHA512: diff --git a/include/privacy_pass/crypto/voprf.hpp b/include/privacy_pass/crypto/voprf.hpp index 91e5fba..8c11d95 100644 --- a/include/privacy_pass/crypto/voprf.hpp +++ b/include/privacy_pass/crypto/voprf.hpp @@ -12,7 +12,7 @@ namespace privacy_pass::crypto { // Supports P-384 with SHA-384 // P-384 constants -constexpr size_t P384_ELEMENT_SIZE = 97; // Uncompressed point (1 + 48 + 48) +constexpr size_t P384_ELEMENT_SIZE = 49; // Compressed point (1 + 48) constexpr size_t P384_SCALAR_SIZE = 48; constexpr size_t P384_OUTPUT_SIZE = 48; constexpr size_t P384_PROOF_SIZE = 96; // 2 * scalar size diff --git a/include/privacy_pass/http/auth_scheme.hpp b/include/privacy_pass/http/auth_scheme.hpp index a898f2c..08ffba2 100644 --- a/include/privacy_pass/http/auth_scheme.hpp +++ b/include/privacy_pass/http/auth_scheme.hpp @@ -9,13 +9,14 @@ #include #include +#include namespace privacy_pass::http { // WWW-Authenticate header parameters struct ChallengeHeader { std::string challenge; // Base64url-encoded TokenChallenge - std::string token_key; // Base64url-encoded issuer public key + std::optional token_key; // Base64url-encoded issuer public key std::optional max_age; // Optional max-age in seconds // Format as WWW-Authenticate header value @@ -24,6 +25,9 @@ struct ChallengeHeader { // Parse from WWW-Authenticate header value [[nodiscard]] static Result parse(std::string_view header); + // Parse all PrivateToken challenges from a WWW-Authenticate header value + [[nodiscard]] static Result> parse_all(std::string_view header); + // Decode the challenge [[nodiscard]] Result decode_challenge() const; diff --git a/justfile b/justfile index 81c7390..05c76ad 100644 --- a/justfile +++ b/justfile @@ -1,7 +1,5 @@ # Privacy Pass C++ - Build Commands -set shell := ["zsh", "-cu"] - default: @just --list @@ -57,7 +55,7 @@ format-check: # Static analysis lint: - clang-tidy src/**/*.cpp -- -I include -std=c++20 + find src -name '*.cpp' -exec clang-tidy {} -- -I include -std=c++23 \; # Generate compile_commands.json for IDE support compile-commands: configure diff --git a/src/core/origin.cpp b/src/core/origin.cpp index b718eef..ee16949 100644 --- a/src/core/origin.cpp +++ b/src/core/origin.cpp @@ -442,11 +442,8 @@ Result Origin::verify( "No RSA keys configured"}); case TokenType::VOPRF_P384_SHA384: - if (impl_->private_origin) { - return impl_->private_origin->validate_structure(token, expected_challenge); - } - return std::unexpected(Error{ErrorCode::ISSUER_UNKNOWN, - "No VOPRF keys configured"}); + return std::unexpected(Error{ErrorCode::UNSUPPORTED_TOKEN_TYPE, + "VOPRF tokens require issuer private-key verification"}); default: return std::unexpected(Error{ErrorCode::UNSUPPORTED_TOKEN_TYPE, diff --git a/src/core/serialization.cpp b/src/core/serialization.cpp index 4e93bf0..1b0afec 100644 --- a/src/core/serialization.cpp +++ b/src/core/serialization.cpp @@ -71,14 +71,28 @@ std::string encode(ByteView data) { return result; } +std::string encode_padded(ByteView data) { + std::string result = encode(data); + while ((result.size() % 4) != 0) { + result.push_back('='); + } + return result; +} + Result decode(std::string_view encoded) { if (encoded.empty()) { return Bytes{}; } - // Remove padding if present + size_t padding = 0; while (!encoded.empty() && encoded.back() == '=') { encoded.remove_suffix(1); + ++padding; + } + + if (padding > 2 || encoded.find('=') != std::string_view::npos || encoded.size() % 4 == 1) { + return std::unexpected(Error{ErrorCode::INVALID_BASE64, + "Invalid base64url padding"}); } if (encoded.empty()) { diff --git a/src/core/token.cpp b/src/core/token.cpp index d66f2eb..8a807e0 100644 --- a/src/core/token.cpp +++ b/src/core/token.cpp @@ -152,6 +152,10 @@ Result Token::deserialize(ByteView data) { } token.authenticator.assign(auth->begin(), auth->end()); + if (!reader.empty()) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Trailing Token data"}); + } + return token; } diff --git a/src/core/token_challenge.cpp b/src/core/token_challenge.cpp index 7ef662a..3f1ea94 100644 --- a/src/core/token_challenge.cpp +++ b/src/core/token_challenge.cpp @@ -33,7 +33,7 @@ std::string TokenChallenge::origin_info_string() const { size_t TokenChallenge::serialized_size() const noexcept { size_t size = 2; // token_type size += 2 + issuer_name.size(); // length-prefixed issuer_name - + size += 1; // redemption_context length if (redemption_context) { size += 32; // Fixed 32 bytes } @@ -45,6 +45,17 @@ size_t TokenChallenge::serialized_size() const noexcept { } Result TokenChallenge::serialize() const { + if (issuer_name.empty() || issuer_name.size() > 0xFFFF) { + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "issuer_name length must be 1..65535"}); + } + + std::string origin_str = origin_info_string(); + if (origin_str.size() > 0xFFFF) { + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "origin_info length must be <= 65535"}); + } + ByteWriter writer(serialized_size()); // token_type (2 bytes) @@ -56,13 +67,15 @@ Result TokenChallenge::serialize() const { reinterpret_cast(issuer_name.data()), issuer_name.size())); - // redemption_context (0 or 32 bytes) + // redemption_context (length-prefixed, 0 or 32 bytes) if (redemption_context) { + writer.write_u8(32); writer.write_array(*redemption_context); + } else { + writer.write_u8(0); } // origin_info (length-prefixed, 2 bytes) - std::string origin_str = origin_info_string(); writer.write_u16(static_cast(origin_str.size())); if (!origin_str.empty()) { writer.write_bytes(ByteView( @@ -99,109 +112,56 @@ Result TokenChallenge::deserialize(ByteView data) { reinterpret_cast(issuer_bytes->data()), issuer_bytes->size()); - // Per RFC 9578: The format is: - // struct { - // uint16 token_type; - // opaque issuer_name<1..2^16-1>; - // opaque redemption_context<0..32>; // 0 or 32 bytes - // opaque origin_info<0..2^16-1>; - // } - // - // redemption_context is either 0 or 32 bytes. - // We need to parse based on token type semantics and remaining data. - // - // Strategy: Calculate expected remaining size with and without context. - // If remaining >= 34 (32 context + 2 origin_len), try to read context first. - // If remaining == 2 (just origin_len of 0), no context. - - size_t remaining = reader.remaining(); - - if (remaining < 2) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Missing origin_info length"}); - } - - // Peek at what would be origin_len if there's no redemption_context - size_t saved_pos = reader.position(); - - // First, try parsing WITHOUT redemption_context - auto potential_origin_len = reader.read_u16(); - if (!potential_origin_len) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read potential origin_len"}); - } - - // Check if remaining data (after this u16) matches the length we just read - size_t remaining_after_len = reader.remaining(); - - if (remaining_after_len == *potential_origin_len) { - // This looks like the correct interpretation: no redemption_context - // Read origin_info - if (*potential_origin_len > 0) { - auto origin_bytes = reader.read_bytes(*potential_origin_len); - if (!origin_bytes) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read origin_info"}); - } - - std::string origin_str( - reinterpret_cast(origin_bytes->data()), - origin_bytes->size()); - - // Parse comma-separated origins - size_t pos = 0; - while (pos < origin_str.size()) { - size_t comma = origin_str.find(',', pos); - if (comma == std::string::npos) { - challenge.origin_info.push_back(origin_str.substr(pos)); - break; - } - challenge.origin_info.push_back(origin_str.substr(pos, comma - pos)); - pos = comma + 1; - } - } - return challenge; + auto context_len = reader.read_u8(); + if (!context_len) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read redemption_context length"}); } - // Reset and try WITH redemption_context - // We need to re-read from the saved position - // Unfortunately ByteReader doesn't have seek, so we recreate from remaining - ByteReader reader2(data.subspan(saved_pos)); + if (*context_len != 0 && *context_len != 32) { + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "redemption_context length must be 0 or 32"}); + } - if (reader2.remaining() >= 34) { // 32 (context) + 2 (origin_len min) - auto context = reader2.read_array<32>(); - if (context) { - challenge.redemption_context = *context; + if (*context_len == 32) { + auto context = reader.read_array<32>(); + if (!context) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read redemption_context"}); } + challenge.redemption_context = *context; + } - auto origin_len = reader2.read_u16(); - if (!origin_len) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read origin_info length"}); + auto origin_len = reader.read_u16(); + if (!origin_len) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read origin_info length"}); + } + + if (*origin_len > 0) { + auto origin_bytes = reader.read_bytes(*origin_len); + if (!origin_bytes) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read origin_info"}); } - if (*origin_len > 0) { - auto origin_bytes = reader2.read_bytes(*origin_len); - if (!origin_bytes) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read origin_info"}); - } + std::string origin_str( + reinterpret_cast(origin_bytes->data()), + origin_bytes->size()); - std::string origin_str( - reinterpret_cast(origin_bytes->data()), - origin_bytes->size()); - - // Parse comma-separated origins - size_t pos = 0; - while (pos < origin_str.size()) { - size_t comma = origin_str.find(',', pos); - if (comma == std::string::npos) { - challenge.origin_info.push_back(origin_str.substr(pos)); - break; - } - challenge.origin_info.push_back(origin_str.substr(pos, comma - pos)); - pos = comma + 1; + size_t pos = 0; + while (pos < origin_str.size()) { + size_t comma = origin_str.find(',', pos); + if (comma == std::string::npos) { + challenge.origin_info.push_back(origin_str.substr(pos)); + break; } + challenge.origin_info.push_back(origin_str.substr(pos, comma - pos)); + pos = comma + 1; } - return challenge; } - return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Could not determine redemption_context presence"}); + if (!reader.empty()) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Trailing TokenChallenge data"}); + } + + return challenge; } Result TokenChallenge::digest() const { diff --git a/src/core/token_provider.cpp b/src/core/token_provider.cpp index 3411058..2024c89 100644 --- a/src/core/token_provider.cpp +++ b/src/core/token_provider.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include @@ -48,6 +50,31 @@ struct TokenProvider::Impl { } }; +namespace { + +bool ascii_equal_case_insensitive(std::string_view a, std::string_view b) { + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), + [](char lhs, char rhs) { + return std::tolower(static_cast(lhs)) == + std::tolower(static_cast(rhs)); + }); +} + +bool origin_info_allows(std::string_view origin, const std::vector& origin_info) { + if (origin_info.empty()) { + return true; + } + if (origin.empty()) { + return false; + } + return std::any_of(origin_info.begin(), origin_info.end(), + [&](const std::string& candidate) { + return ascii_equal_case_insensitive(origin, candidate); + }); +} + +} // namespace + TokenProvider::TokenProvider(TokenProviderConfig config) : impl_(std::make_unique(std::move(config))) {} @@ -82,6 +109,11 @@ Result TokenProvider::prepare_request( std::lock_guard lock(impl_->mutex); + if (!origin_info_allows(impl_->config.origin_name, challenge.origin_info)) { + return std::unexpected(Error{ErrorCode::INVALID_CHALLENGE, + "Challenge origin_info does not include current origin"}); + } + auto key = impl_->find_key(challenge.issuer_name, challenge.token_type); if (!key) { return std::unexpected(Error{ErrorCode::ISSUER_UNKNOWN, @@ -166,7 +198,7 @@ Result TokenProvider::finalize( // Find the key used for this request for (const auto& [issuer, keys] : impl_->issuer_keys) { for (const auto& key : keys) { - if (key.type == response.token_type) { + if (key.type == response.token_type && key.key_id == context.finalization_data.token_key_id) { auto rsa_key = crypto::BlindRsaPublicKey::from_spki( ByteView(key.data.data(), key.data.size())); if (!rsa_key) { @@ -181,7 +213,7 @@ Result TokenProvider::finalize( } else if (response.token_type == TokenType::VOPRF_P384_SHA384) { for (const auto& [issuer, keys] : impl_->issuer_keys) { for (const auto& key : keys) { - if (key.type == response.token_type) { + if (key.type == response.token_type && key.key_id == context.finalization_data.token_key_id) { auto voprf_key = crypto::VoprfPublicKey::from_bytes( ByteView(key.data.data(), key.data.size())); if (!voprf_key) { diff --git a/src/core/token_request.cpp b/src/core/token_request.cpp index a798aad..680c4eb 100644 --- a/src/core/token_request.cpp +++ b/src/core/token_request.cpp @@ -64,22 +64,31 @@ Result TokenRequest::deserialize(ByteView data) { request.blinded_msg.assign(blinded->begin(), blinded->end()); + if (!reader.empty()) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Trailing TokenRequest data"}); + } + return request; } // BatchedTokenRequest implementation size_t BatchedTokenRequest::serialized_size() const noexcept { - size_t size = varint_size(requests.size()); + size_t payload_size = 0; for (const auto& req : requests) { - size += req.serialized_size(); + payload_size += req.serialized_size(); } - return size; + return varint_size(payload_size) + payload_size; } Result BatchedTokenRequest::serialize() const { ByteWriter writer(serialized_size()); - writer.write_varint(requests.size()); + size_t payload_size = 0; + for (const auto& req : requests) { + payload_size += req.serialized_size(); + } + writer.write_varint(payload_size); + for (const auto& req : requests) { auto serialized = req.serialize(); if (!serialized) { @@ -92,41 +101,62 @@ Result BatchedTokenRequest::serialize() const { } // Maximum batch size to prevent memory exhaustion -constexpr uint64_t MAX_BATCH_SIZE = 10000; +constexpr size_t MAX_BATCH_SIZE = 10000; +constexpr uint64_t MAX_BATCH_PAYLOAD_SIZE = 65535; Result BatchedTokenRequest::deserialize(ByteView data) { ByteReader reader(data); - auto count = reader.read_varint(); - if (!count) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read request count"}); + auto payload_size_value = reader.read_varint(); + if (!payload_size_value) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read batch size"}); + } + if (*payload_size_value > MAX_BATCH_PAYLOAD_SIZE) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Batch payload too large"}); } - // Enforce maximum batch size - if (*count > MAX_BATCH_SIZE) { - return std::unexpected(Error{ErrorCode::MALFORMED_DATA, - "Batch size exceeds maximum: " + std::to_string(*count)}); + auto payload = reader.read_bytes(static_cast(*payload_size_value)); + if (!payload) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read batch payload"}); } + if (!reader.empty()) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Trailing BatchedTokenRequest data"}); + } + + ByteReader payload_reader(*payload); BatchedTokenRequest result; - result.requests.reserve(static_cast(*count)); - for (uint64_t i = 0; i < *count; ++i) { - // Read type to determine size - auto type_peek = reader.peek_u8(); - if (!type_peek) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to peek token_type"}); + while (!payload_reader.empty()) { + if (result.requests.size() >= MAX_BATCH_SIZE) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, + "Batch size exceeds maximum"}); } - // Parse individual request from remaining data - auto req = TokenRequest::deserialize(reader.remaining_data()); + auto remaining = payload_reader.remaining_data(); + if (remaining.size() < 3) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read request header"}); + } + + const auto type = static_cast( + (static_cast(remaining[0]) << 8) | remaining[1]); + const auto info = TokenTypeInfo::for_type(type); + if (info.blinded_element_size == 0) { + return std::unexpected(Error{ErrorCode::UNSUPPORTED_TOKEN_TYPE, + "Unsupported batched request token type"}); + } + + const size_t request_size = 2 + 1 + info.blinded_element_size; + auto request_bytes = payload_reader.read_bytes(request_size); + if (!request_bytes) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read request"}); + } + + auto req = TokenRequest::deserialize(*request_bytes); if (!req) { return std::unexpected(req.error()); } - // Skip past the request we just parsed - reader.skip(req->serialized_size()); - result.requests.push_back(std::move(*req)); } diff --git a/src/core/token_response.cpp b/src/core/token_response.cpp index 6fd04d0..978093b 100644 --- a/src/core/token_response.cpp +++ b/src/core/token_response.cpp @@ -32,16 +32,16 @@ Result VoprfTokenResponse::serialize() const { } Result VoprfTokenResponse::deserialize(ByteView data) { - constexpr size_t EXPECTED_SIZE = 97 + 96; // Ne + 2*Ns for P-384 + constexpr size_t EXPECTED_SIZE = 49 + 96; // Ne + 2*Ns for P-384 if (data.size() != EXPECTED_SIZE) { return std::unexpected(Error{ErrorCode::INVALID_LENGTH, - "VOPRF response must be 193 bytes"}); + "VOPRF response must be 145 bytes"}); } VoprfTokenResponse response; - response.evaluate_msg.assign(data.begin(), data.begin() + 97); - response.evaluate_proof.assign(data.begin() + 97, data.end()); + response.evaluate_msg.assign(data.begin(), data.begin() + 49); + response.evaluate_proof.assign(data.begin() + 49, data.end()); return response; } @@ -131,6 +131,10 @@ Result OptionalTokenResponse::deserialize(ByteView data) OptionalTokenResponse result; result.present = (*status == 0x01); + if (*status != 0x00 && *status != 0x01) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Invalid response status"}); + } + if (result.present) { auto type = reader.read_u16(); if (!type) { @@ -144,6 +148,8 @@ Result OptionalTokenResponse::deserialize(ByteView data) return std::unexpected(resp.error()); } result.response = std::move(*resp); + } else if (!reader.empty()) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Trailing absent response data"}); } return result; @@ -153,7 +159,17 @@ Result OptionalTokenResponse::deserialize(ByteView data) Result BatchedTokenResponse::serialize() const { ByteWriter writer; - writer.write_varint(responses.size()); + size_t payload_size = 0; + for (const auto& resp : responses) { + auto serialized = resp.serialize(); + if (!serialized) { + return std::unexpected(serialized.error()); + } + payload_size += serialized->size(); + } + + writer.write_varint(payload_size); + for (const auto& resp : responses) { auto serialized = resp.serialize(); if (!serialized) { @@ -166,37 +182,69 @@ Result BatchedTokenResponse::serialize() const { } // Maximum batch size to prevent memory exhaustion -constexpr uint64_t MAX_RESPONSE_BATCH_SIZE = 10000; +constexpr size_t MAX_RESPONSE_BATCH_SIZE = 10000; +constexpr uint64_t MAX_RESPONSE_BATCH_PAYLOAD_SIZE = 65535; Result BatchedTokenResponse::deserialize(ByteView data) { ByteReader reader(data); - auto count = reader.read_varint(); - if (!count) { - return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read response count"}); + auto payload_size_value = reader.read_varint(); + if (!payload_size_value) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read response batch size"}); + } + if (*payload_size_value > MAX_RESPONSE_BATCH_PAYLOAD_SIZE) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Response batch payload too large"}); } - // Enforce maximum batch size - if (*count > MAX_RESPONSE_BATCH_SIZE) { - return std::unexpected(Error{ErrorCode::MALFORMED_DATA, - "Response batch size exceeds maximum: " + std::to_string(*count)}); + auto payload = reader.read_bytes(static_cast(*payload_size_value)); + if (!payload) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read response batch payload"}); + } + if (!reader.empty()) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Trailing BatchedTokenResponse data"}); } + ByteReader payload_reader(*payload); + BatchedTokenResponse result; - result.responses.reserve(static_cast(*count)); - for (uint64_t i = 0; i < *count; ++i) { - auto resp = OptionalTokenResponse::deserialize(reader.remaining_data()); - if (!resp) { - return std::unexpected(resp.error()); + while (!payload_reader.empty()) { + if (result.responses.size() >= MAX_RESPONSE_BATCH_SIZE) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, + "Response batch size exceeds maximum"}); + } + + auto remaining = payload_reader.remaining_data(); + if (remaining.empty()) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read response status"}); + } + + size_t response_size = 1; + if (remaining[0] == 0x01) { + if (remaining.size() < 3) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read response type"}); + } + const auto type = static_cast( + (static_cast(remaining[1]) << 8) | remaining[2]); + const auto info = TokenTypeInfo::for_type(type); + if (info.authenticator_size == 0) { + return std::unexpected(Error{ErrorCode::UNSUPPORTED_TOKEN_TYPE, + "Unsupported batched response token type"}); + } + response_size += 2 + (type == TokenType::VOPRF_P384_SHA384 ? 145 : info.authenticator_size); + } else if (remaining[0] != 0x00) { + return std::unexpected(Error{ErrorCode::MALFORMED_DATA, "Invalid response status"}); + } + + auto response_bytes = payload_reader.read_bytes(response_size); + if (!response_bytes) { + return std::unexpected(Error{ErrorCode::UNEXPECTED_END, "Failed to read response"}); } - // Calculate how much to skip (simplified - real implementation needs proper length handling) - size_t skip = 1; // status byte - if (resp->present && resp->response) { - skip += 2 + resp->response->serialized_size(); + auto resp = OptionalTokenResponse::deserialize(*response_bytes); + if (!resp) { + return std::unexpected(resp.error()); } - reader.skip(skip); result.responses.push_back(std::move(*resp)); } diff --git a/src/crypto/blind_rsa.cpp b/src/crypto/blind_rsa.cpp index 823a6dc..2cefc27 100644 --- a/src/crypto/blind_rsa.cpp +++ b/src/crypto/blind_rsa.cpp @@ -41,81 +41,100 @@ std::string get_sanitized_error() { return "Cryptographic operation failed"; } -// EMSA-PSS encoding for blind RSA (RFC 9474) -Result emsa_pss_encode(ByteView msg, size_t emLen) { - // Hash the message with SHA-384 - auto mHash = sha384(msg); - if (!mHash) { - return std::unexpected(mHash.error()); - } +bool is_rsa_pss_key(const EVP_PKEY* key) { + return EVP_PKEY_base_id(key) == EVP_PKEY_RSA_PSS; +} - // Generate random salt - auto salt_result = random_bytes(SALT_LENGTH); - if (!salt_result) { - return std::unexpected(salt_result.error()); - } - const auto& salt = *salt_result; - - // Build M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt - Bytes m_prime(8 + 48 + SALT_LENGTH); - std::fill(m_prime.begin(), m_prime.begin() + 8, 0); - std::copy(mHash->begin(), mHash->end(), m_prime.begin() + 8); - std::copy(salt.begin(), salt.end(), m_prime.begin() + 8 + 48); - - // H = Hash(M') - auto H = sha384(ByteView(m_prime.data(), m_prime.size())); - if (!H) { - return std::unexpected(H.error()); - } - - // Generate MGF1 mask - size_t dbLen = emLen - 48 - 1; - Bytes DB(dbLen); - - // PS = zeros, DB = PS || 0x01 || salt - std::fill(DB.begin(), DB.end() - SALT_LENGTH - 1, 0); - DB[dbLen - SALT_LENGTH - 1] = 0x01; - std::copy(salt.begin(), salt.end(), DB.end() - SALT_LENGTH); - - // MGF1 with SHA-384 - Bytes dbMask(dbLen); - size_t counter = 0; - size_t offset = 0; - while (offset < dbLen) { - Bytes c_input(48 + 4); - std::copy(H->begin(), H->end(), c_input.begin()); - c_input[48] = static_cast(counter >> 24); - c_input[49] = static_cast(counter >> 16); - c_input[50] = static_cast(counter >> 8); - c_input[51] = static_cast(counter); - - auto c_hash = sha384(ByteView(c_input.data(), c_input.size())); - if (!c_hash) { - return std::unexpected(c_hash.error()); - } +bool digest_name_is_sha384(std::string_view name) { + return name == "SHA384" || name == "SHA-384" || name == "SHA2-384"; +} - size_t to_copy = std::min(static_cast(48), dbLen - offset); - std::copy(c_hash->begin(), c_hash->begin() + to_copy, dbMask.begin() + offset); - offset += to_copy; - counter++; +Result validate_rsa_pss_params(const EVP_PKEY* key) { + if (!is_rsa_pss_key(key)) { + return std::unexpected(Error{ErrorCode::INVALID_KEY, "Not an RSASSA-PSS key"}); } - // maskedDB = DB XOR dbMask - Bytes maskedDB(dbLen); - for (size_t i = 0; i < dbLen; i++) { - maskedDB[i] = DB[i] ^ dbMask[i]; + BIGNUM* n = nullptr; + BIGNUM* e = nullptr; + if (EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_N, &n) != 1 || + EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_E, &e) != 1) { + BN_free(n); + BN_free(e); + return std::unexpected(Error{ErrorCode::INVALID_KEY, "Missing RSA key parameters"}); } - // Clear top bits - maskedDB[0] &= 0x7F; + const bool key_params_ok = BN_num_bits(n) == RSA_BITS && BN_is_word(e, RSA_PUBLIC_EXPONENT) == 1; + BN_free(n); + BN_free(e); + if (!key_params_ok) { + return std::unexpected(Error{ErrorCode::INVALID_KEY, + "RSASSA-PSS key must use RSA-2048 and exponent 65537"}); + } + + int salt_len = 0; + if (EVP_PKEY_get_int_param(key, OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, &salt_len) != 1 || + salt_len != SALT_LENGTH) { + return std::unexpected(Error{ErrorCode::INVALID_KEY, + "RSASSA-PSS key must use 48-byte salt"}); + } + + char digest[80]{}; + char mgf1_digest[80]{}; + size_t digest_len = 0; + size_t mgf1_digest_len = 0; + if (EVP_PKEY_get_utf8_string_param( + key, OSSL_PKEY_PARAM_RSA_DIGEST, digest, sizeof(digest), &digest_len) != 1 || + EVP_PKEY_get_utf8_string_param( + key, OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, mgf1_digest, sizeof(mgf1_digest), + &mgf1_digest_len) != 1 || + !digest_name_is_sha384(std::string_view(digest, digest_len)) || + !digest_name_is_sha384(std::string_view(mgf1_digest, mgf1_digest_len))) { + return std::unexpected(Error{ErrorCode::INVALID_KEY, + "RSASSA-PSS key must use SHA-384 and MGF1-SHA-384"}); + } + + return {}; +} - // EM = maskedDB || H || 0xbc - Bytes EM(emLen); - std::copy(maskedDB.begin(), maskedDB.end(), EM.begin()); - std::copy(H->begin(), H->end(), EM.begin() + dbLen); - EM[emLen - 1] = 0xBC; +// EMSA-PSS encoding for blind RSA (RFC 9474) +Result emsa_pss_encode(EVP_PKEY* pkey, ByteView msg) { + auto mHash = sha384(msg); + if (!mHash) { + return std::unexpected(mHash.error()); + } - return EM; +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4996) +#elif defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + RSA* rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) { + return std::unexpected(Error{ErrorCode::INVALID_KEY, "Failed to get RSA key"}); + } + + Bytes encoded(static_cast(RSA_size(rsa))); + const int ok = RSA_padding_add_PKCS1_PSS_mgf1( + rsa, + encoded.data(), + mHash->data(), + EVP_sha384(), + EVP_sha384(), + SALT_LENGTH); + RSA_free(rsa); +#if defined(_MSC_VER) +#pragma warning(pop) +#elif defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + + if (ok != 1) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, get_sanitized_error()}); + } + + return encoded; } } // namespace @@ -123,6 +142,7 @@ Result emsa_pss_encode(ByteView msg, size_t emLen) { // BlindRsaPublicKey implementation struct BlindRsaPublicKey::Impl { EVP_PKEY* pkey = nullptr; + Bytes original_spki; TokenKeyId cached_key_id{}; bool key_id_computed = false; @@ -153,10 +173,13 @@ Result BlindRsaPublicKey::from_spki(ByteView spki) { return std::unexpected(Error{ErrorCode::INVALID_KEY, get_sanitized_error()}); } - if (EVP_PKEY_base_id(key.impl_->pkey) != EVP_PKEY_RSA) { - return std::unexpected(Error{ErrorCode::INVALID_KEY, "Not an RSA key"}); + auto params = validate_rsa_pss_params(key.impl_->pkey); + if (!params) { + return std::unexpected(params.error()); } + key.impl_->original_spki.assign(spki.begin(), spki.end()); + return key; } @@ -177,20 +200,47 @@ Result BlindRsaPublicKey::from_components(ByteView modulus, B return std::unexpected(Error{ErrorCode::INVALID_KEY, "Failed to create bignums"}); } - EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); - if (!ctx) { + OSSL_PARAM_BLD* bld = OSSL_PARAM_BLD_new(); + if (!bld) { BN_free(n); BN_free(e); - return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create context"}); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create parameter builder"}); + } + + const bool params_pushed = + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) == 1 && + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e) == 1 && + OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_RSA_DIGEST, + const_cast("SHA384"), 0) == 1 && + OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_RSA_MASKGENFUNC, + const_cast("MGF1"), 0) == 1 && + OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, + const_cast("SHA384"), 0) == 1 && + OSSL_PARAM_BLD_push_int(bld, OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, SALT_LENGTH) == 1; + if (!params_pushed) { + OSSL_PARAM_BLD_free(bld); + BN_free(n); + BN_free(e); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to build RSA parameters"}); } - OSSL_PARAM_BLD* bld = OSSL_PARAM_BLD_new(); - OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n); - OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e); OSSL_PARAM* params = OSSL_PARAM_BLD_to_param(bld); + if (!params) { + OSSL_PARAM_BLD_free(bld); + BN_free(n); + BN_free(e); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create RSA parameters"}); + } EVP_PKEY* pkey = nullptr; - EVP_PKEY_CTX* from_data_ctx = EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr); + EVP_PKEY_CTX* from_data_ctx = EVP_PKEY_CTX_new_from_name(nullptr, "RSA-PSS", nullptr); + if (!from_data_ctx) { + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + BN_free(n); + BN_free(e); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create RSA-PSS context"}); + } bool success = EVP_PKEY_fromdata_init(from_data_ctx) == 1 && EVP_PKEY_fromdata(from_data_ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) == 1; @@ -198,12 +248,17 @@ Result BlindRsaPublicKey::from_components(ByteView modulus, B OSSL_PARAM_free(params); OSSL_PARAM_BLD_free(bld); EVP_PKEY_CTX_free(from_data_ctx); - EVP_PKEY_CTX_free(ctx); BN_free(n); BN_free(e); if (!success || !pkey) { - return std::unexpected(Error{ErrorCode::INVALID_KEY, "Failed to create RSA key"}); + return std::unexpected(Error{ErrorCode::INVALID_KEY, "Failed to create RSASSA-PSS key"}); + } + + auto validated = validate_rsa_pss_params(pkey); + if (!validated) { + EVP_PKEY_free(pkey); + return std::unexpected(validated.error()); } key.impl_->pkey = pkey; @@ -234,12 +289,18 @@ Result BlindRsaPublicKey::key_id() const { return impl_->cached_key_id; } - auto spki = to_spki(); - if (!spki) { - return std::unexpected(spki.error()); + Bytes spki; + if (!impl_->original_spki.empty()) { + spki = impl_->original_spki; + } else { + auto encoded = to_spki(); + if (!encoded) { + return std::unexpected(encoded.error()); + } + spki = std::move(*encoded); } - auto hash = sha256(ByteView(spki->data(), spki->size())); + auto hash = sha256(ByteView(spki.data(), spki.size())); if (!hash) { return std::unexpected(hash.error()); } @@ -264,7 +325,7 @@ Result BlindRsaPublicKey::blind(ByteView msg) const { int mod_size = BN_num_bytes(n_bn); // EMSA-PSS encode the message - auto encoded = emsa_pss_encode(msg, static_cast(mod_size)); + auto encoded = emsa_pss_encode(impl_->pkey, msg); if (!encoded) { BN_free(n_bn); return std::unexpected(encoded.error()); @@ -298,81 +359,129 @@ Result BlindRsaPublicKey::blind(ByteView msg) const { BIGNUM* r = BN_new(); BIGNUM* r_inv = BN_new(); BIGNUM* x = BN_new(); + BIGNUM* x_mont = BN_new(); BIGNUM* blinded = BN_new(); + BN_MONT_CTX* mont = BN_MONT_CTX_new(); - if (!r || !r_inv || !x || !blinded) { + if (!r || !r_inv || !x || !x_mont || !blinded || !mont) { BN_free(n_bn); BN_free(m); BN_free(e_bn); - BN_free(r); - BN_free(r_inv); + BN_clear_free(r); + BN_clear_free(r_inv); BN_free(x); + BN_free(x_mont); BN_free(blinded); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to allocate bignums"}); } + if (BN_MONT_CTX_set(mont, n_bn, bn_ctx) != 1) { + BN_free(n_bn); + BN_free(m); + BN_free(e_bn); + BN_clear_free(r); + BN_clear_free(r_inv); + BN_free(x); + BN_free(x_mont); + BN_free(blinded); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create Montgomery context"}); + } + // Generate r coprime to n do { if (!BN_rand_range(r, n_bn)) { BN_free(n_bn); BN_free(m); BN_free(e_bn); - BN_free(r); - BN_free(r_inv); + BN_clear_free(r); + BN_clear_free(r_inv); BN_free(x); + BN_free(x_mont); BN_free(blinded); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::BLINDING_FAILED, "Failed to generate r"}); } } while (BN_is_zero(r) || !BN_mod_inverse(r_inv, r, n_bn, bn_ctx)); // x = r^e mod n - if (!BN_mod_exp(x, r, e_bn, n_bn, bn_ctx)) { + if (BN_mod_exp_mont(x, r, e_bn, n_bn, bn_ctx, mont) != 1 || + BN_to_montgomery(x_mont, x, mont, bn_ctx) != 1) { BN_free(n_bn); BN_free(m); BN_free(e_bn); - BN_free(r); - BN_free(r_inv); + BN_clear_free(r); + BN_clear_free(r_inv); BN_free(x); + BN_free(x_mont); BN_free(blinded); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::BLINDING_FAILED, "Failed to compute x"}); } // blinded = m * x mod n - if (!BN_mod_mul(blinded, m, x, n_bn, bn_ctx)) { + if (BN_mod_mul_montgomery(blinded, m, x_mont, mont, bn_ctx) != 1) { BN_free(n_bn); BN_free(m); BN_free(e_bn); - BN_free(r); - BN_free(r_inv); + BN_clear_free(r); + BN_clear_free(r_inv); BN_free(x); + BN_free(x_mont); BN_free(blinded); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::BLINDING_FAILED, "Failed to blind message"}); } BlindingData result; - // Store inverse - result.inverse.resize(BN_num_bytes(r_inv)); - BN_bn2bin(r_inv, result.inverse.data()); + // Store inverse in standard form for portable serialization. + result.inverse.resize(static_cast(mod_size)); + if (BN_bn2binpad(r_inv, result.inverse.data(), mod_size) != mod_size) { + BN_free(n_bn); + BN_free(m); + BN_free(e_bn); + BN_clear_free(r); + BN_clear_free(r_inv); + BN_free(x); + BN_free(x_mont); + BN_free(blinded); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::BLINDING_FAILED, "Failed to serialize inverse"}); + } // Store blinded message result.blinded_msg.resize(static_cast(mod_size)); - int blinded_len = BN_bn2binpad(blinded, result.blinded_msg.data(), mod_size); - if (blinded_len != mod_size) { - result.blinded_msg.resize(static_cast(blinded_len)); + if (BN_bn2binpad(blinded, result.blinded_msg.data(), mod_size) != mod_size) { + BN_free(n_bn); + BN_free(m); + BN_free(e_bn); + BN_clear_free(r); + BN_clear_free(r_inv); + BN_free(x); + BN_free(x_mont); + BN_free(blinded); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::BLINDING_FAILED, "Failed to serialize blinded message"}); } BN_free(n_bn); BN_free(m); BN_free(e_bn); - BN_free(r); - BN_free(r_inv); + BN_clear_free(r); + BN_clear_free(r_inv); BN_free(x); + BN_free(x_mont); BN_free(blinded); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return result; @@ -400,6 +509,13 @@ Result BlindRsaPublicKey::finalize( } int mod_size = BN_num_bytes(n_bn); + const auto expected_size = static_cast(mod_size); + + if (blind_sig.size() != expected_size || blinding_data.inverse.size() != expected_size) { + BN_free(n_bn); + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "Blind signature and inverse must match modulus length"}); + } BN_CTX* bn_ctx = BN_CTX_new(); if (!bn_ctx) { @@ -410,23 +526,64 @@ Result BlindRsaPublicKey::finalize( BIGNUM* z = BN_bin2bn(blind_sig.data(), static_cast(blind_sig.size()), nullptr); BIGNUM* r_inv = BN_bin2bn(blinding_data.inverse.data(), static_cast(blinding_data.inverse.size()), nullptr); + BIGNUM* r_inv_mont = BN_new(); BIGNUM* sig = BN_new(); + BN_MONT_CTX* mont = BN_MONT_CTX_new(); - if (!z || !r_inv || !sig) { + if (!z || !r_inv || !r_inv_mont || !sig || !mont) { BN_free(n_bn); BN_free(z); BN_clear_free(r_inv); + BN_clear_free(r_inv_mont); BN_free(sig); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to allocate bignums"}); } + // Reject blind signatures outside [0, n) + if (BN_cmp(z, n_bn) >= 0) { + BN_free(n_bn); + BN_free(z); + BN_clear_free(r_inv); + BN_clear_free(r_inv_mont); + BN_free(sig); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::UNBLINDING_FAILED, "Blind signature out of range"}); + } + + if (BN_MONT_CTX_set(mont, n_bn, bn_ctx) != 1) { + BN_free(n_bn); + BN_free(z); + BN_clear_free(r_inv); + BN_clear_free(r_inv_mont); + BN_free(sig); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create Montgomery context"}); + } + + // Convert standard-form inverse to Montgomery form for constant-shape multiply + if (BN_to_montgomery(r_inv_mont, r_inv, mont, bn_ctx) != 1) { + BN_free(n_bn); + BN_free(z); + BN_clear_free(r_inv); + BN_clear_free(r_inv_mont); + BN_free(sig); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::UNBLINDING_FAILED, "Failed to convert inverse to Montgomery form"}); + } + // sig = z * r_inv mod n - if (!BN_mod_mul(sig, z, r_inv, n_bn, bn_ctx)) { + if (BN_mod_mul_montgomery(sig, z, r_inv_mont, mont, bn_ctx) != 1) { BN_free(n_bn); BN_free(z); BN_clear_free(r_inv); + BN_clear_free(r_inv_mont); BN_free(sig); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::UNBLINDING_FAILED, "Failed to unblind"}); } @@ -436,8 +593,10 @@ Result BlindRsaPublicKey::finalize( BN_free(n_bn); BN_free(z); - BN_clear_free(r_inv); // Use clear_free for sensitive data + BN_clear_free(r_inv); + BN_clear_free(r_inv_mont); BN_free(sig); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); // Clear the blinding data after successful use to prevent reuse @@ -512,13 +671,16 @@ BlindRsaPrivateKey::BlindRsaPrivateKey(BlindRsaPrivateKey&&) noexcept = default; BlindRsaPrivateKey& BlindRsaPrivateKey::operator=(BlindRsaPrivateKey&&) noexcept = default; Result> BlindRsaPrivateKey::generate() { - EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, nullptr); if (!ctx) { return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create context"}); } bool success = EVP_PKEY_keygen_init(ctx) == 1 && - EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_BITS) == 1; + EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_BITS) == 1 && + EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx, EVP_sha384()) == 1 && + EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx, EVP_sha384()) == 1 && + EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx, SALT_LENGTH) == 1; EVP_PKEY* pkey = nullptr; success = success && EVP_PKEY_keygen(ctx, &pkey) == 1; @@ -551,12 +713,17 @@ Result BlindRsaPrivateKey::from_pkcs8(ByteView pkcs8) { BlindRsaPrivateKey key; const uint8_t* p = pkcs8.data(); - key.impl_->pkey = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &p, static_cast(pkcs8.size())); + key.impl_->pkey = d2i_AutoPrivateKey(nullptr, &p, static_cast(pkcs8.size())); if (!key.impl_->pkey) { return std::unexpected(Error{ErrorCode::INVALID_KEY, get_sanitized_error()}); } + auto params = validate_rsa_pss_params(key.impl_->pkey); + if (!params) { + return std::unexpected(params.error()); + } + return key; } @@ -615,31 +782,62 @@ Result BlindRsaPrivateKey::blind_sign(ByteView blinded_msg) const { if (!n_bn || !d_bn) { BN_free(n_bn); - BN_free(d_bn); + BN_clear_free(d_bn); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get key parameters"}); } int mod_size = BN_num_bytes(n_bn); + if (blinded_msg.size() != static_cast(mod_size)) { + BN_free(n_bn); + BN_clear_free(d_bn); + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "Blinded message must match RSA modulus size"}); + } BN_CTX* bn_ctx = BN_CTX_new(); + BN_MONT_CTX* mont = BN_MONT_CTX_new(); BIGNUM* m = BN_bin2bn(blinded_msg.data(), static_cast(blinded_msg.size()), nullptr); BIGNUM* sig = BN_new(); - if (!bn_ctx || !m || !sig) { + if (!bn_ctx || !mont || !m || !sig) { BN_free(n_bn); - BN_free(d_bn); + BN_clear_free(d_bn); BN_free(m); BN_free(sig); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to allocate bignums"}); } + if (BN_is_zero(m) || BN_cmp(m, n_bn) >= 0) { + BN_free(n_bn); + BN_clear_free(d_bn); + BN_free(m); + BN_free(sig); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "Blinded message representative out of range"}); + } + + if (BN_MONT_CTX_set(mont, n_bn, bn_ctx) != 1) { + BN_free(n_bn); + BN_clear_free(d_bn); + BN_free(m); + BN_free(sig); + BN_MONT_CTX_free(mont); + BN_CTX_free(bn_ctx); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create Montgomery context"}); + } + // sig = m^d mod n - if (!BN_mod_exp(sig, m, d_bn, n_bn, bn_ctx)) { + BN_set_flags(d_bn, BN_FLG_CONSTTIME); + if (BN_mod_exp_mont_consttime(sig, m, d_bn, n_bn, bn_ctx, mont) != 1) { BN_free(n_bn); - BN_free(d_bn); + BN_clear_free(d_bn); BN_free(m); BN_free(sig); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Blind sign failed"}); } @@ -648,9 +846,10 @@ Result BlindRsaPrivateKey::blind_sign(ByteView blinded_msg) const { BN_bn2binpad(sig, result.data(), mod_size); BN_free(n_bn); - BN_free(d_bn); + BN_clear_free(d_bn); BN_free(m); BN_free(sig); + BN_MONT_CTX_free(mont); BN_CTX_free(bn_ctx); return result; diff --git a/src/crypto/voprf.cpp b/src/crypto/voprf.cpp index 64d25c2..2fcd9ed 100644 --- a/src/crypto/voprf.cpp +++ b/src/crypto/voprf.cpp @@ -17,9 +17,14 @@ namespace privacy_pass::crypto { namespace { -// Domain separation tags for hash-to-curve and DLEQ per RFC 9497 -constexpr const char* DST_H2C = "VOPRF10-P384-SHA384-SSWU-RO"; -constexpr const char* DST_CHALLENGE = "Challenge"; +// Domain separation tags for OPRF v1, VOPRF mode, P-384/SHA-384. +constexpr std::string_view CONTEXT_STRING = "OPRFV1-\x01-P384-SHA384"; +constexpr std::string_view DST_H2C = "HashToGroup-OPRFV1-\x01-P384-SHA384"; +constexpr std::string_view DST_H2S = "HashToScalar-OPRFV1-\x01-P384-SHA384"; +constexpr std::string_view DST_CHALLENGE = "Challenge"; +constexpr std::string_view DST_COMPOSITE = "Composite"; +constexpr std::string_view DST_SEED_PREFIX = "Seed-"; +constexpr std::string_view DST_FINALIZE = "Finalize"; // Maximum input size for OpenSSL APIs (prevent integer truncation) constexpr size_t MAX_INPUT_SIZE = static_cast(INT_MAX); @@ -29,6 +34,9 @@ class P384Group { public: static EC_GROUP* get() { static P384Group instance; + if (!instance.group_) { + return nullptr; + } return instance.group_; } @@ -41,9 +49,6 @@ class P384Group { private: P384Group() { group_ = EC_GROUP_new_by_curve_name(NID_secp384r1); - if (!group_) { - spdlog::error("Failed to create P-384 group"); - } } P384Group(const P384Group&) = delete; @@ -65,14 +70,45 @@ bool constant_time_compare(ByteView a, ByteView b) { return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0; } -// Serialize EC point to uncompressed form (per RFC 9497) +void append_u16(Bytes& out, size_t value) { + out.push_back(static_cast(value >> 8)); + out.push_back(static_cast(value)); +} + +void append_u16_len_prefixed(Bytes& out, ByteView data) { + append_u16(out, data.size()); + out.insert(out.end(), data.begin(), data.end()); +} + +Result finalize_output(ByteView input, ByteView issued_element) { + constexpr size_t MAX_U16 = 0xFFFF; + if (input.size() > MAX_U16 || issued_element.size() > MAX_U16) { + return std::unexpected(Error{ErrorCode::INVALID_LENGTH, + "VOPRF finalize input too large"}); + } + + Bytes hash_input; + hash_input.reserve(2 + input.size() + 2 + issued_element.size() + DST_FINALIZE.size()); + append_u16_len_prefixed(hash_input, input); + append_u16_len_prefixed(hash_input, issued_element); + hash_input.insert(hash_input.end(), DST_FINALIZE.begin(), DST_FINALIZE.end()); + + auto output = sha384(ByteView(hash_input.data(), hash_input.size())); + if (!output) { + return std::unexpected(output.error()); + } + + return Bytes(output->begin(), output->end()); +} + +// Serialize EC point to compressed form (per RFC 9497 SerializeElement) Result point_to_bytes(const EC_POINT* point, const EC_GROUP* group) { BN_CTX* ctx = BN_CTX_new(); if (!ctx) { return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to create BN context"}); } - size_t len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, + size_t len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, nullptr, 0, ctx); if (len == 0) { @@ -81,7 +117,7 @@ Result point_to_bytes(const EC_POINT* point, const EC_GROUP* group) { } Bytes result(len); - if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, + if (EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, result.data(), len, ctx) != len) { BN_CTX_free(ctx); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to serialize point"}); @@ -91,9 +127,9 @@ Result point_to_bytes(const EC_POINT* point, const EC_GROUP* group) { return result; } -// Deserialize EC point with validation (accepts both compressed and uncompressed forms) +// Deserialize EC point with RFC 9497 P-384 validation. EC_POINT* bytes_to_point(ByteView data, const EC_GROUP* group) { - if (data.size() > MAX_INPUT_SIZE) { + if (data.size() != P384_ELEMENT_SIZE || (data[0] != 0x02 && data[0] != 0x03)) { return nullptr; } @@ -114,8 +150,9 @@ EC_POINT* bytes_to_point(ByteView data, const EC_GROUP* group) { return nullptr; } - // Validate point is on the curve - if (EC_POINT_is_on_curve(group, point, ctx) != 1) { + // Validate point is on the curve and not the identity. + if (EC_POINT_is_on_curve(group, point, ctx) != 1 || + EC_POINT_is_at_infinity(group, point) == 1) { EC_POINT_free(point); BN_CTX_free(ctx); return nullptr; @@ -381,8 +418,8 @@ Result hash_to_curve(ByteView input, const EC_GROUP* group) { // Expand message to get 2 field elements (each 72 bytes for security margin) size_t L = 72; // ceil((ceil(log2(p)) + k) / 8) where k=128 - auto expanded = expand_message_xmd(input, ByteView(reinterpret_cast(DST_H2C), - strlen(DST_H2C)), 2 * L); + auto expanded = expand_message_xmd(input, ByteView( + reinterpret_cast(DST_H2C.data()), DST_H2C.size()), 2 * L); if (!expanded) { return std::unexpected(expanded.error()); } @@ -449,55 +486,123 @@ Result hash_to_curve(ByteView input, const EC_GROUP* group) { return R; } -// Compute DLEQ challenge per RFC 9497 Section 3.3.2 -Result compute_dleq_challenge( +Result hash_to_scalar(ByteView input, const EC_GROUP* group, BN_CTX* ctx) { + constexpr size_t L = 72; + auto uniform = expand_message_xmd(input, + ByteView(reinterpret_cast(DST_H2S.data()), DST_H2S.size()), L); + if (!uniform) { + return std::unexpected(uniform.error()); + } + + BIGNUM* order = BN_new(); + BIGNUM* scalar = BN_bin2bn(uniform->data(), static_cast(uniform->size()), nullptr); + if (!order || !scalar || EC_GROUP_get_order(group, order, ctx) != 1 || + BN_mod(scalar, scalar, order, ctx) != 1) { + BN_free(order); + BN_free(scalar); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to hash to scalar"}); + } + + BN_free(order); + return scalar; +} + +void append_string(Bytes& out, std::string_view value) { + out.insert(out.end(), value.begin(), value.end()); +} + +Result compute_composite_scalar( const EC_GROUP* group, - const EC_POINT* A, const EC_POINT* B, const EC_POINT* C, const EC_POINT* D, BN_CTX* ctx) { - // c = H(G, Y, A, B, C, D) where G is generator, Y is public key - // For VOPRF: A=blinded, B=evaluated, C=G, D=Y - - const EC_POINT* G = EC_GROUP_get0_generator(group); + auto Bm = point_to_bytes(B, group); + auto Ci = point_to_bytes(C, group); + auto Di = point_to_bytes(D, group); + if (!Bm || !Ci || !Di) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to serialize composite inputs"}); + } - Bytes input; + Bytes seed_dst; + append_string(seed_dst, DST_SEED_PREFIX); + append_string(seed_dst, CONTEXT_STRING); - // Serialize all points - auto G_bytes = point_to_bytes(G, group); - auto A_bytes = point_to_bytes(A, group); - auto B_bytes = point_to_bytes(B, group); - auto C_bytes = point_to_bytes(C, group); - auto D_bytes = point_to_bytes(D, group); + Bytes seed_transcript; + append_u16_len_prefixed(seed_transcript, ByteView(Bm->data(), Bm->size())); + append_u16_len_prefixed(seed_transcript, ByteView(seed_dst.data(), seed_dst.size())); - if (!G_bytes || !A_bytes || !B_bytes || !C_bytes || !D_bytes) { - return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to serialize points"}); + auto seed = sha384(ByteView(seed_transcript.data(), seed_transcript.size())); + if (!seed) { + return std::unexpected(seed.error()); } - // Concatenate: DST || G || Y || A || B || C || D - input.insert(input.end(), reinterpret_cast(DST_CHALLENGE), - reinterpret_cast(DST_CHALLENGE) + strlen(DST_CHALLENGE)); - input.insert(input.end(), G_bytes->begin(), G_bytes->end()); - input.insert(input.end(), C_bytes->begin(), C_bytes->end()); // C is public key Y - input.insert(input.end(), A_bytes->begin(), A_bytes->end()); - input.insert(input.end(), B_bytes->begin(), B_bytes->end()); - input.insert(input.end(), D_bytes->begin(), D_bytes->end()); + Bytes composite_transcript; + append_u16_len_prefixed(composite_transcript, ByteView(seed->data(), seed->size())); + append_u16(composite_transcript, 0); + append_u16_len_prefixed(composite_transcript, ByteView(Ci->data(), Ci->size())); + append_u16_len_prefixed(composite_transcript, ByteView(Di->data(), Di->size())); + append_string(composite_transcript, DST_COMPOSITE); - auto hash = sha384(ByteView(input.data(), input.size())); - if (!hash) { - return std::unexpected(hash.error()); + return hash_to_scalar(ByteView(composite_transcript.data(), composite_transcript.size()), group, ctx); +} + +Result> compute_composites( + const EC_GROUP* group, + const EC_POINT* B, + const EC_POINT* C, + const EC_POINT* D, + BN_CTX* ctx) { + + auto di_result = compute_composite_scalar(group, B, C, D, ctx); + if (!di_result) { + return std::unexpected(di_result.error()); } + BIGNUM* di = *di_result; - BIGNUM* order = BN_new(); - EC_GROUP_get_order(group, order, ctx); + EC_POINT* M = EC_POINT_new(group); + EC_POINT* Z = EC_POINT_new(group); + if (!M || !Z || + EC_POINT_mul(group, M, nullptr, C, di, ctx) != 1 || + EC_POINT_mul(group, Z, nullptr, D, di, ctx) != 1) { + EC_POINT_free(M); + EC_POINT_free(Z); + BN_free(di); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to compute composites"}); + } - BIGNUM* c = BN_bin2bn(hash->data(), static_cast(hash->size()), nullptr); - BN_mod(c, c, order, ctx); - BN_free(order); + BN_free(di); + return std::make_pair(M, Z); +} - return c; +Result compute_dleq_challenge( + const EC_GROUP* group, + const EC_POINT* B, + const EC_POINT* M, + const EC_POINT* Z, + const EC_POINT* t2, + const EC_POINT* t3, + BN_CTX* ctx) { + + auto Bm = point_to_bytes(B, group); + auto a0 = point_to_bytes(M, group); + auto a1 = point_to_bytes(Z, group); + auto a2 = point_to_bytes(t2, group); + auto a3 = point_to_bytes(t3, group); + if (!Bm || !a0 || !a1 || !a2 || !a3) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to serialize challenge inputs"}); + } + + Bytes transcript; + append_u16_len_prefixed(transcript, ByteView(Bm->data(), Bm->size())); + append_u16_len_prefixed(transcript, ByteView(a0->data(), a0->size())); + append_u16_len_prefixed(transcript, ByteView(a1->data(), a1->size())); + append_u16_len_prefixed(transcript, ByteView(a2->data(), a2->size())); + append_u16_len_prefixed(transcript, ByteView(a3->data(), a3->size())); + append_string(transcript, DST_CHALLENGE); + + return hash_to_scalar(ByteView(transcript.data(), transcript.size()), group, ctx); } // Generate DLEQ proof per RFC 9497 @@ -510,11 +615,24 @@ Result generate_dleq_proof( BN_CTX* ctx) { BIGNUM* order = BN_new(); - EC_GROUP_get_order(group, order, ctx); + if (!order || EC_GROUP_get_order(group, order, ctx) != 1) { + BN_free(order); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get group order"}); + } + + auto composites = compute_composites(group, Y, R, Z, ctx); + if (!composites) { + BN_free(order); + return std::unexpected(composites.error()); + } + EC_POINT* M = composites->first; + EC_POINT* composite_Z = composites->second; // Generate random scalar t BIGNUM* t = BN_new(); if (!BN_rand_range(t, order) || BN_is_zero(t)) { + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(order); BN_free(t); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to generate random scalar"}); @@ -524,26 +642,31 @@ Result generate_dleq_proof( EC_POINT* A = EC_POINT_new(group); if (!A || EC_POINT_mul(group, A, t, nullptr, nullptr, ctx) != 1) { EC_POINT_free(A); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(order); BN_free(t); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to compute A"}); } - // B = t * R + // B = t * M EC_POINT* B = EC_POINT_new(group); - if (!B || EC_POINT_mul(group, B, nullptr, R, t, ctx) != 1) { + if (!B || EC_POINT_mul(group, B, nullptr, M, t, ctx) != 1) { EC_POINT_free(A); EC_POINT_free(B); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(order); BN_free(t); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to compute B"}); } - // c = H(G, Y, R, Z, A, B) - auto c_result = compute_dleq_challenge(group, R, Z, Y, A, ctx); + auto c_result = compute_dleq_challenge(group, Y, M, composite_Z, A, B, ctx); if (!c_result) { EC_POINT_free(A); EC_POINT_free(B); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(order); BN_free(t); return std::unexpected(c_result.error()); @@ -563,6 +686,8 @@ Result generate_dleq_proof( EC_POINT_free(A); EC_POINT_free(B); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(order); BN_free(t); BN_free(c); @@ -599,40 +724,71 @@ Result verify_dleq_proof( return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to parse proof"}); } + // Validate scalars are in range [0, order-1] + BIGNUM* order = BN_new(); + if (!order || EC_GROUP_get_order(group, order, ctx) != 1) { + BN_free(c); + BN_free(s); + BN_free(order); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get group order"}); + } + + if (BN_cmp(c, order) >= 0 || BN_cmp(s, order) >= 0) { + BN_free(c); + BN_free(s); + BN_free(order); + return std::unexpected(Error{ErrorCode::VERIFICATION_FAILED, "Proof scalars out of range"}); + } + BN_free(order); + + auto composites = compute_composites(group, Y, R, Z, ctx); + if (!composites) { + BN_free(c); + BN_free(s); + return std::unexpected(composites.error()); + } + EC_POINT* M = composites->first; + EC_POINT* composite_Z = composites->second; + // A' = s * G + c * Y (G is the generator, implicit in EC_POINT_mul with non-null first scalar) EC_POINT* A_prime = EC_POINT_new(group); if (!A_prime || EC_POINT_mul(group, A_prime, s, Y, c, ctx) != 1) { EC_POINT_free(A_prime); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(c); BN_free(s); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to compute A'"}); } - // B' = s * R + c * Z - EC_POINT* sR = EC_POINT_new(group); + // B' = s * M + c * Z + EC_POINT* sM = EC_POINT_new(group); EC_POINT* cZ = EC_POINT_new(group); EC_POINT* B_prime = EC_POINT_new(group); - if (!sR || !cZ || !B_prime || - EC_POINT_mul(group, sR, nullptr, R, s, ctx) != 1 || - EC_POINT_mul(group, cZ, nullptr, Z, c, ctx) != 1 || - EC_POINT_add(group, B_prime, sR, cZ, ctx) != 1) { + if (!sM || !cZ || !B_prime || + EC_POINT_mul(group, sM, nullptr, M, s, ctx) != 1 || + EC_POINT_mul(group, cZ, nullptr, composite_Z, c, ctx) != 1 || + EC_POINT_add(group, B_prime, sM, cZ, ctx) != 1) { EC_POINT_free(A_prime); - EC_POINT_free(sR); + EC_POINT_free(sM); EC_POINT_free(cZ); EC_POINT_free(B_prime); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(c); BN_free(s); return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to compute B'"}); } - // c' = H(G, Y, R, Z, A', B') - auto c_prime_result = compute_dleq_challenge(group, R, Z, Y, A_prime, ctx); + auto c_prime_result = compute_dleq_challenge(group, Y, M, composite_Z, A_prime, B_prime, ctx); if (!c_prime_result) { EC_POINT_free(A_prime); - EC_POINT_free(sR); + EC_POINT_free(sM); EC_POINT_free(cZ); EC_POINT_free(B_prime); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(c); BN_free(s); return std::unexpected(c_prime_result.error()); @@ -651,9 +807,11 @@ Result verify_dleq_proof( ByteView(c_prime_bytes.data(), c_prime_bytes.size())); EC_POINT_free(A_prime); - EC_POINT_free(sR); + EC_POINT_free(sM); EC_POINT_free(cZ); EC_POINT_free(B_prime); + EC_POINT_free(M); + EC_POINT_free(composite_Z); BN_free(c); BN_free(s); BN_free(c_prime); @@ -686,6 +844,9 @@ VoprfPublicKey& VoprfPublicKey::operator=(VoprfPublicKey&&) noexcept = default; Result VoprfPublicKey::from_bytes(ByteView data) { VoprfPublicKey key; + if (!key.impl_->group) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } key.impl_->point = bytes_to_point(data, key.impl_->group); if (!key.impl_->point) { @@ -699,6 +860,9 @@ Result VoprfPublicKey::to_bytes() const { if (!impl_->point) { return std::unexpected(Error{ErrorCode::INVALID_KEY, "Key not initialized"}); } + if (!impl_->group) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } return point_to_bytes(impl_->point, impl_->group); } @@ -748,6 +912,9 @@ VoprfPrivateKey& VoprfPrivateKey::operator=(VoprfPrivateKey&&) noexcept = defaul Result> VoprfPrivateKey::generate() { VoprfPrivateKey private_key; + if (!private_key.impl_->group) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } BN_CTX* ctx = BN_CTX_new(); if (!ctx) { @@ -755,7 +922,11 @@ Result> VoprfPrivateKey::generate() { } BIGNUM* order = BN_new(); - EC_GROUP_get_order(private_key.impl_->group, order, ctx); + if (!order || EC_GROUP_get_order(private_key.impl_->group, order, ctx) != 1) { + BN_free(order); + BN_CTX_free(ctx); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get group order"}); + } private_key.impl_->scalar = BN_new(); if (!BN_rand_range(private_key.impl_->scalar, order) || @@ -777,6 +948,12 @@ Result> VoprfPrivateKey::generate() { } VoprfPublicKey public_key; + if (!public_key.impl_->group) { + EC_POINT_free(pub_point); + BN_free(order); + BN_CTX_free(ctx); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } public_key.impl_->point = pub_point; BN_free(order); @@ -786,22 +963,32 @@ Result> VoprfPrivateKey::generate() { } Result VoprfPrivateKey::from_bytes(ByteView data) { - if (data.empty()) { - return std::unexpected(Error{ErrorCode::INVALID_KEY, "Empty private key data"}); - } - - // Validate input size to prevent integer truncation - if (data.size() > MAX_INPUT_SIZE) { - return std::unexpected(Error{ErrorCode::INVALID_KEY, "Private key data too large"}); + if (data.size() != P384_SCALAR_SIZE) { + return std::unexpected(Error{ErrorCode::INVALID_KEY, "Invalid private key length"}); } VoprfPrivateKey key; + if (!key.impl_->group) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } key.impl_->scalar = BN_bin2bn(data.data(), static_cast(data.size()), nullptr); if (!key.impl_->scalar) { return std::unexpected(Error{ErrorCode::INVALID_KEY, "Failed to parse private key"}); } + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* order = BN_new(); + if (!ctx || !order || EC_GROUP_get_order(key.impl_->group, order, ctx) != 1 || + BN_is_zero(key.impl_->scalar) || BN_cmp(key.impl_->scalar, order) >= 0) { + BN_CTX_free(ctx); + BN_free(order); + return std::unexpected(Error{ErrorCode::INVALID_KEY, "Invalid private key scalar"}); + } + + BN_CTX_free(ctx); + BN_free(order); + return key; } @@ -823,6 +1010,9 @@ Result VoprfPrivateKey::public_key() const { if (!impl_->scalar) { return std::unexpected(Error{ErrorCode::INVALID_KEY, "Key not initialized"}); } + if (!impl_->group) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } BN_CTX* ctx = BN_CTX_new(); if (!ctx) { @@ -840,6 +1030,10 @@ Result VoprfPrivateKey::public_key() const { BN_CTX_free(ctx); VoprfPublicKey public_key; + if (!public_key.impl_->group) { + EC_POINT_free(pub_point); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } public_key.impl_->point = pub_point; return public_key; @@ -865,6 +1059,9 @@ VoprfClient& VoprfClient::operator=(VoprfClient&&) noexcept = default; Result VoprfClient::blind(ByteView input) const { EC_GROUP* group = get_p384_group(); + if (!group) { + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get curve group"}); + } // Hash input to curve point auto h_result = hash_to_curve(input, group); @@ -881,7 +1078,12 @@ Result VoprfClient::blind(ByteView input) const { // Generate random blinding scalar r BIGNUM* order = BN_new(); - EC_GROUP_get_order(group, order, ctx); + if (!order || EC_GROUP_get_order(group, order, ctx) != 1) { + BN_free(order); + BN_CTX_free(ctx); + EC_POINT_free(P); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get group order"}); + } BIGNUM* r = BN_new(); if (!BN_rand_range(r, order) || BN_is_zero(r)) { @@ -1024,7 +1226,13 @@ Result VoprfClient::finalize( // Compute r^-1 BIGNUM* order = BN_new(); - EC_GROUP_get_order(group, order, ctx); + if (!order || EC_GROUP_get_order(group, order, ctx) != 1) { + BN_clear_free(r); + BN_free(order); + BN_CTX_free(ctx); + EC_POINT_free(Z); + return std::unexpected(Error{ErrorCode::CRYPTO_ERROR, "Failed to get group order"}); + } BIGNUM* r_inv = BN_mod_inverse(nullptr, r, order, ctx); if (!r_inv) { @@ -1061,13 +1269,9 @@ Result VoprfClient::finalize( return std::unexpected(output_point.error()); } - // Hash point to final output - auto final_output = sha384(ByteView(output_point->data(), output_point->size())); - if (!final_output) { - return std::unexpected(final_output.error()); - } - - return Bytes(final_output->begin(), final_output->end()); + return finalize_output( + ByteView(finalization_data.input.data(), finalization_data.input.size()), + ByteView(output_point->data(), output_point->size())); } // VoprfServer implementation @@ -1261,7 +1465,7 @@ Result VoprfServer::verify_finalize(ByteView input, ByteView output) const return std::unexpected(expected_bytes.error()); } - auto expected_hash = sha384(ByteView(expected_bytes->data(), expected_bytes->size())); + auto expected_hash = finalize_output(input, ByteView(expected_bytes->data(), expected_bytes->size())); // Clear sensitive data scalar_bytes->clear(); diff --git a/src/http/auth_scheme.cpp b/src/http/auth_scheme.cpp index 177ad1b..ff65caf 100644 --- a/src/http/auth_scheme.cpp +++ b/src/http/auth_scheme.cpp @@ -48,6 +48,7 @@ std::optional> parse_auth_param(std::string_ // Quoted string with escape handling per RFC 7230 Section 3.2.6 input.remove_prefix(1); bool escaped = false; + bool closed_quote = false; while (!input.empty()) { char c = input[0]; @@ -61,14 +62,15 @@ std::optional> parse_auth_param(std::string_ escaped = true; } else if (c == '"') { // End of quoted string + closed_quote = true; break; } else { value.push_back(c); } } - if (escaped) { - // Trailing backslash without following character + if (escaped || !closed_quote) { + // Trailing backslash or missing closing quote return std::nullopt; } } else { @@ -95,16 +97,58 @@ std::optional> parse_auth_param(std::string_ return std::make_pair(std::move(key), std::move(value)); } +std::vector split_private_token_challenges(std::string_view header) { + std::vector challenges; + bool in_quote = false; + bool escaped = false; + size_t start = 0; + + for (size_t i = 0; i < header.size(); ++i) { + const char c = header[i]; + if (escaped) { + escaped = false; + continue; + } + if (in_quote && c == '\\') { + escaped = true; + continue; + } + if (c == '"') { + in_quote = !in_quote; + continue; + } + if (in_quote || c != ',') { + continue; + } + + size_t next = i + 1; + while (next < header.size() && (header[next] == ' ' || header[next] == '\t')) { + ++next; + } + if (header.substr(next).starts_with("PrivateToken ")) { + challenges.push_back(header.substr(start, i - start)); + start = next; + } + } + + challenges.push_back(header.substr(start)); + return challenges; +} + } // namespace // ChallengeHeader implementation std::string ChallengeHeader::format() const { std::string result = "PrivateToken challenge=\""; result += challenge; - result += "\", token-key=\""; - result += token_key; result += "\""; + if (token_key) { + result += ", token-key=\""; + result += *token_key; + result += "\""; + } + if (max_age) { result += ", max-age="; result += std::to_string(*max_age); @@ -114,46 +158,58 @@ std::string ChallengeHeader::format() const { } Result ChallengeHeader::parse(std::string_view header) { - // Check for PrivateToken scheme - if (!header.starts_with("PrivateToken ")) { + auto all = parse_all(header); + if (!all) { + return std::unexpected(all.error()); + } + if (all->empty()) { return std::unexpected(Error{ErrorCode::INVALID_HEADER, - "Not a PrivateToken header"}); + "No PrivateToken challenge"}); } + return std::move((*all)[0]); +} - header.remove_prefix(13); // "PrivateToken " - - ChallengeHeader result; +Result> ChallengeHeader::parse_all(std::string_view header) { + std::vector results; - while (!header.empty()) { - auto param = parse_auth_param(header); - if (!param) { - break; + for (auto challenge : split_private_token_challenges(header)) { + if (!challenge.starts_with("PrivateToken ")) { + return std::unexpected(Error{ErrorCode::INVALID_HEADER, + "Not a PrivateToken header"}); } - if (param->first == "challenge") { - result.challenge = std::move(param->second); - } else if (param->first == "token-key") { - result.token_key = std::move(param->second); - } else if (param->first == "max-age") { - try { - result.max_age = static_cast(std::stoul(param->second)); - } catch (...) { - // Ignore invalid max-age + challenge.remove_prefix(13); // "PrivateToken " + + ChallengeHeader result; + + while (!challenge.empty()) { + auto param = parse_auth_param(challenge); + if (!param) { + break; + } + + if (param->first == "challenge") { + result.challenge = std::move(param->second); + } else if (param->first == "token-key") { + result.token_key = std::move(param->second); + } else if (param->first == "max-age") { + try { + result.max_age = static_cast(std::stoul(param->second)); + } catch (...) { + // Ignore invalid max-age + } } } - } - if (result.challenge.empty()) { - return std::unexpected(Error{ErrorCode::MISSING_PARAMETER, - "Missing challenge parameter"}); - } + if (result.challenge.empty()) { + return std::unexpected(Error{ErrorCode::MISSING_PARAMETER, + "Missing challenge parameter"}); + } - if (result.token_key.empty()) { - return std::unexpected(Error{ErrorCode::MISSING_PARAMETER, - "Missing token-key parameter"}); + results.push_back(std::move(result)); } - return result; + return results; } Result ChallengeHeader::decode_challenge() const { @@ -166,7 +222,11 @@ Result ChallengeHeader::decode_challenge() const { } Result ChallengeHeader::decode_token_key() const { - return base64url::decode(token_key); + if (!token_key) { + return std::unexpected(Error{ErrorCode::MISSING_PARAMETER, + "Missing token-key parameter"}); + } + return base64url::decode(*token_key); } // AuthorizationHeader implementation @@ -224,8 +284,8 @@ Result build_www_authenticate( } ChallengeHeader header; - header.challenge = base64url::encode(ByteView(serialized->data(), serialized->size())); - header.token_key = base64url::encode(token_key); + header.challenge = base64url::encode_padded(ByteView(serialized->data(), serialized->size())); + header.token_key = base64url::encode_padded(token_key); header.max_age = max_age; return header.format(); @@ -238,7 +298,7 @@ Result build_authorization(const Token& token) { } AuthorizationHeader header; - header.token = base64url::encode(ByteView(serialized->data(), serialized->size())); + header.token = base64url::encode_padded(ByteView(serialized->data(), serialized->size())); return header.format(); } diff --git a/tests/test_blind_rsa.cpp b/tests/test_blind_rsa.cpp index 25b75b8..97c64a7 100644 --- a/tests/test_blind_rsa.cpp +++ b/tests/test_blind_rsa.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: BSD-2-Clause #include #include +#include + +#include "test_vector_utils.hpp" using namespace privacy_pass; using namespace privacy_pass::crypto; @@ -111,6 +114,46 @@ TEST_SUITE("Blind RSA") { REQUIRE(valid.has_value()); CHECK(*valid == true); } + + SUBCASE("Reject overlong blind signature") { + auto blinding = public_key.blind(msg); + REQUIRE(blinding.has_value()); + + auto blind_sig = private_key.blind_sign( + ByteView(blinding->blinded_msg.data(), blinding->blinded_msg.size())); + REQUIRE(blind_sig.has_value()); + + Bytes overlong_blind_sig; + overlong_blind_sig.reserve(blind_sig->size() + 1); + overlong_blind_sig.push_back(0); + overlong_blind_sig.insert(overlong_blind_sig.end(), blind_sig->begin(), blind_sig->end()); + + auto signature = public_key.finalize( + ByteView(overlong_blind_sig.data(), overlong_blind_sig.size()), + *blinding, + msg); + CHECK(!signature.has_value()); + } + + SUBCASE("Reject wrong-length blinding inverse") { + auto blinding = public_key.blind(msg); + REQUIRE(blinding.has_value()); + + auto blind_sig = private_key.blind_sign( + ByteView(blinding->blinded_msg.data(), blinding->blinded_msg.size())); + REQUIRE(blind_sig.has_value()); + + BlindingData bad_blinding; + bad_blinding.inverse = SecureBytes(blinding->inverse.view()); + bad_blinding.inverse.resize(blinding->inverse.size() - 1); + bad_blinding.blinded_msg = blinding->blinded_msg; + + auto signature = public_key.finalize( + ByteView(blind_sig->data(), blind_sig->size()), + bad_blinding, + msg); + CHECK(!signature.has_value()); + } } TEST_CASE("Verification fails for wrong message") { @@ -150,4 +193,55 @@ TEST_SUITE("Blind RSA") { CHECK(!result.has_value()); } } + + TEST_CASE("RFC 9578 public-verifiable token vectors verify") { + const std::array files{ + "pub_verif_rfc9578.go.json", + "pub_verif_rfc9578.rust.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + auto public_key = BlindRsaPublicKey::from_spki( + test_vectors::view(test_vectors::hex_field(vector, "pkS"))); + REQUIRE(public_key.has_value()); + + const auto token_bytes = test_vectors::hex_field(vector, "token"); + auto token = Token::deserialize(test_vectors::view(token_bytes)); + REQUIRE(token.has_value()); + + auto key_id = public_key->key_id(); + REQUIRE(key_id.has_value()); + CHECK(*key_id == token->token_key_id); + + auto input = token->authenticator_input().serialize(); + REQUIRE(input.has_value()); + auto valid = public_key->verify( + test_vectors::view(*input), + test_vectors::view(token->authenticator)); + REQUIRE(valid.has_value()); + CHECK(*valid); + } + } + } + + TEST_CASE("RFC 9474 4096-bit vectors are outside Privacy Pass profile") { + const auto vectors = test_vectors::load_json("blindrsa_rfc9474_sha384_pss.json"); + + for (const auto& vector : vectors) { + CAPTURE(vector.at("name").get()); + + const auto modulus = test_vectors::hex_field(vector, "n"); + CHECK(modulus.size() == 512); + + auto public_key = BlindRsaPublicKey::from_components( + test_vectors::view(modulus), + test_vectors::view(test_vectors::hex_field(vector, "e"))); + CHECK(!public_key.has_value()); + } + } } diff --git a/tests/test_client.cpp b/tests/test_client.cpp index cbb040c..9208ca5 100644 --- a/tests/test_client.cpp +++ b/tests/test_client.cpp @@ -47,7 +47,7 @@ TEST_SUITE("PrivateClient") { REQUIRE(result.has_value()); CHECK(result->request.token_type == TokenType::VOPRF_P384_SHA384); - CHECK(result->request.blinded_msg.size() == 97); // P-384 element + CHECK(result->request.blinded_msg.size() == 49); // P-384 element } } diff --git a/tests/test_data/auth_scheme_header_complicated.json b/tests/test_data/auth_scheme_header_complicated.json new file mode 100644 index 0000000..c6fbab9 --- /dev/null +++ b/tests/test_data/auth_scheme_header_complicated.json @@ -0,0 +1,9 @@ +[ + { + "token-type-0": 2, + "token-key-0": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "max-age-0": 10, + "token-challenge-0": "0002000e6973737565722e6578616d706c65208a3e83a33d98005d2f30bef419fa6bf4cd5c6005e36b1285bbb4ccd40fa4b383000e6f726967696e2e6578616d706c65", + "WWW-Authenticate": "PrivateToken challenge=AAIADmlzc3Vlci5leGFtcGxlIIo-g6M9mABdLzC-9Bn6a_TNXGAF42sShbu0zNQPpLODAA5vcmlnaW4uZXhhbXBsZQ==, token-key=MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEAyxrta2qV9bHOATpM_KsluUsuZKIwNOQlCn6rQ8DfOowSmTrxKxEZCNS0cb7DHUtsmtnN2pBhKi7pA1I-beWiJNawLwnlw3TQz-Adj1KcUAp4ovZ5CPpoK1orQwyB6vGvcte155T8mKMTknaHl1fORTtSbvm_bOuZl5uEI7kPRGGiKvN6qwz1cz91l6vkTTHHMttooYHGy75gfYwOUuBlX9mZbcWE7KC-h6-814ozfRex26noKLvYHikTFxROf_ifVWGXCbCWy7nqR0zq0mTCBz_kl0DAHwDhCRBgZpg9IeX4PwhuLoI8h5zUPO9wDSo1Kpur1hLQPK0C2xNLfiJaXwIDAQAB,unknownChallengeAttribute=ignore-me, max-age=10" + } +] \ No newline at end of file diff --git a/tests/test_data/auth_scheme_header_rfc9577.json b/tests/test_data/auth_scheme_header_rfc9577.json new file mode 100644 index 0000000..bc8f515 --- /dev/null +++ b/tests/test_data/auth_scheme_header_rfc9577.json @@ -0,0 +1,20 @@ +[ + { + "token-type-0": 2, + "token-key-0": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "max-age-0": 10, + "token-challenge-0": "0002000e6973737565722e6578616d706c65208a3e83a33d98005d2f30bef419fa6bf4cd5c6005e36b1285bbb4ccd40fa4b383000e6f726967696e2e6578616d706c65", + "WWW-Authenticate": "PrivateToken challenge=\"AAIADmlzc3Vlci5leGFtcGxlIIo-g6M9mABdLzC-9Bn6a_TNXGAF42sShbu0zNQPpLODAA5vcmlnaW4uZXhhbXBsZQ==\", token-key=\"MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEAyxrta2qV9bHOATpM_KsluUsuZKIwNOQlCn6rQ8DfOowSmTrxKxEZCNS0cb7DHUtsmtnN2pBhKi7pA1I-beWiJNawLwnlw3TQz-Adj1KcUAp4ovZ5CPpoK1orQwyB6vGvcte155T8mKMTknaHl1fORTtSbvm_bOuZl5uEI7kPRGGiKvN6qwz1cz91l6vkTTHHMttooYHGy75gfYwOUuBlX9mZbcWE7KC-h6-814ozfRex26noKLvYHikTFxROf_ifVWGXCbCWy7nqR0zq0mTCBz_kl0DAHwDhCRBgZpg9IeX4PwhuLoI8h5zUPO9wDSo1Kpur1hLQPK0C2xNLfiJaXwIDAQAB\",unknownChallengeAttribute=\"ignore-me\", max-age=\"10\"" + }, + { + "token-type-0": 2, + "token-key-0": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "max-age-0": 10, + "token-challenge-0": "0002000e6973737565722e6578616d706c65208a3e83a33d98005d2f30bef419fa6bf4cd5c6005e36b1285bbb4ccd40fa4b383000e6f726967696e2e6578616d706c65", + "token-type-1": 1, + "token-key-1": "ebb1fed338310361c08d0c7576969671296e05e99a17d7926dfc28a53fabd489fac0f82bca86249a668f3a5bfab374c9", + "max-age-1": 10, + "token-challenge-1": "0001000e6973737565722e6578616d706c65208a3e83a33d98005d2f30bef419fa6bf4cd5c6005e36b1285bbb4ccd40fa4b383000e6f726967696e2e6578616d706c65", + "WWW-Authenticate": "PrivateToken challenge=\"AAIADmlzc3Vlci5leGFtcGxlIIo-g6M9mABdLzC-9Bn6a_TNXGAF42sShbu0zNQPpLODAA5vcmlnaW4uZXhhbXBsZQ==\", token-key=\"MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEAyxrta2qV9bHOATpM_KsluUsuZKIwNOQlCn6rQ8DfOowSmTrxKxEZCNS0cb7DHUtsmtnN2pBhKi7pA1I-beWiJNawLwnlw3TQz-Adj1KcUAp4ovZ5CPpoK1orQwyB6vGvcte155T8mKMTknaHl1fORTtSbvm_bOuZl5uEI7kPRGGiKvN6qwz1cz91l6vkTTHHMttooYHGy75gfYwOUuBlX9mZbcWE7KC-h6-814ozfRex26noKLvYHikTFxROf_ifVWGXCbCWy7nqR0zq0mTCBz_kl0DAHwDhCRBgZpg9IeX4PwhuLoI8h5zUPO9wDSo1Kpur1hLQPK0C2xNLfiJaXwIDAQAB\",unknownChallengeAttribute=\"ignore-me\", max-age=\"10\", PrivateToken challenge=\"AAEADmlzc3Vlci5leGFtcGxlIIo-g6M9mABdLzC-9Bn6a_TNXGAF42sShbu0zNQPpLODAA5vcmlnaW4uZXhhbXBsZQ==\", token-key=\"67H-0zgxA2HAjQx1dpaWcSluBemaF9eSbfwopT-r1In6wPgryoYkmmaPOlv6s3TJ\",unknownChallengeAttribute=\"ignore-me\", max-age=\"10\"" + } +] \ No newline at end of file diff --git a/tests/test_data/auth_scheme_token_rfc9577.json b/tests/test_data/auth_scheme_token_rfc9577.json new file mode 100644 index 0000000..db27a44 --- /dev/null +++ b/tests/test_data/auth_scheme_token_rfc9577.json @@ -0,0 +1,52 @@ +[ + { + "comment": "// token_type(0002), issuer_name(issuer.example),\n// origin_info(origin.example), redemption_context(non-empty)", + "token_type": "0002", + "issuer_name": "6973737565722e6578616d706c65", + "redemption_context": "476ac2c935f458e9b2d7af32dacfbd22dd6023ef5887a789f1abe004e79bb5bb", + "origin_info": "6f726967696e2e6578616d706c65", + "nonce": "e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab", + "token_key_id": "ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708", + "token_authenticator_input": "0002e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab8e1d5518ec82964255526efd8f9db88205a8ddd3ffb1db298fcc3ad36c42388fca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708" + }, + { + "comment": "// token_type(0002), issuer_name(issuer.example),\n// origin_info(origin.example), redemption_context(empty)", + "token_type": "0002", + "issuer_name": "6973737565722e6578616d706c65", + "redemption_context": "", + "origin_info": "6f726967696e2e6578616d706c65", + "nonce": "e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab", + "token_key_id": "ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708", + "token_authenticator_input": "0002e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab11e15c91a7c2ad02abd66645802373db1d823bea80f08d452541fb2b62b5898bca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708" + }, + { + "comment": "// token_type(0002), issuer_name(issuer.example),\n// origin_info(), redemption_context(empty)", + "token_type": "0002", + "issuer_name": "6973737565722e6578616d706c65", + "redemption_context": "", + "origin_info": "", + "nonce": "e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab", + "token_key_id": "ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708", + "token_authenticator_input": "0002e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688ababb741ec1b6fd05f1e95f8982906aec1612896d9ca97d53eef94ad3c9fe023f7a4ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708" + }, + { + "comment": "// token_type(0002), issuer_name(issuer.example),\n// origin_info(), redemption_context(non-empty)", + "token_type": "0002", + "issuer_name": "6973737565722e6578616d706c65", + "redemption_context": "476ac2c935f458e9b2d7af32dacfbd22dd6023ef5887a789f1abe004e79bb5bb", + "origin_info": "", + "nonce": "e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab", + "token_key_id": "ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708", + "token_authenticator_input": "0002e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688ababb85fb5bc06edeb0e8e8bdb5b3bea8c4fa40837c82e8bcaf5882c81e14817ea18ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708" + }, + { + "comment": "// token_type(0002), issuer_name(issuer.example),\n// origin_info(foo.example,bar.example),\n// redemption_context(non-empty)", + "token_type": "0002", + "issuer_name": "6973737565722e6578616d706c65", + "redemption_context": "476ac2c935f458e9b2d7af32dacfbd22dd6023ef5887a789f1abe004e79bb5bb", + "origin_info": "666f6f2e6578616d706c652c6261722e6578616d706c65", + "nonce": "e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688abab", + "token_key_id": "ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708", + "token_authenticator_input": "0002e01978182c469e5e026d66558ee186568614f235e41ef7e2378e6f202688ababa2a775866b6ae0f98944910c8f48728d8a2735b9157762ddbf803f70e2e8ba3eca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708" + } +] \ No newline at end of file diff --git a/tests/test_data/blindrsa_rfc9474_sha384_pss.json b/tests/test_data/blindrsa_rfc9474_sha384_pss.json new file mode 100644 index 0000000..1b71aff --- /dev/null +++ b/tests/test_data/blindrsa_rfc9474_sha384_pss.json @@ -0,0 +1,16 @@ +[ + { + "name": "RSABSSA-SHA384-PSS-Randomized", + "n": "aec4d69addc70b990ea66a5e70603b6fee27aafebd08f2d94cbe1250c556e047a928d635c3f45ee9b66d1bc628a03bac9b7c3f416fe20dabea8f3d7b4bbf7f963be335d2328d67e6c13ee4a8f955e05a3283720d3e1f139c38e43e0338ad058a9495c53377fc35be64d208f89b4aa721bf7f7d3fef837be2a80e0f8adf0bcd1eec5bb040443a2b2792fdca522a7472aed74f31a1ebe1eebc1f408660a0543dfe2a850f106a617ec6685573702eaaa21a5640a5dcaf9b74e397fa3af18a2f1b7c03ba91a6336158de420d63188ee143866ee415735d155b7c2d854d795b7bc236cffd71542df34234221a0413e142d8c61355cc44d45bda94204974557ac2704cd8b593f035a5724b1adf442e78c542cd4414fce6f1298182fb6d8e53cef1adfd2e90e1e4deec52999bdc6c29144e8d52a125232c8c6d75c706ea3cc06841c7bda33568c63a6c03817f722b50fcf898237d788a4400869e44d90a3020923dc646388abcc914315215fcd1bae11b1c751fd52443aac8f601087d8d42737c18a3fa11ecd4131ecae017ae0a14acfc4ef85b83c19fed33cfd1cd629da2c4c09e222b398e18d822f77bb378dea3cb360b605e5aa58b20edc29d000a66bd177c682a17e7eb12a63ef7c2e4183e0d898f3d6bf567ba8ae84f84f1d23bf8b8e261c3729e2fa6d07b832e07cddd1d14f55325c6f924267957121902dc19b3b32948bdead5", + "e": "010001", + "input_msg": "8417e699b219d583fb6216ae0c53ca0e9723442d02f1d1a34295527e7d929e8b8f3dc6fb8c4a02f4d6352edf0907822c1210a9b32f9bdda4c45a698c80023aa6b59f8cfec5fdbb36331372ebefedae7d", + "sig": "191e941c57510e22d29afad257de5ca436d2316221fe870c7cb75205a6c071c2735aed0bc24c37f3d5bd960ab97a829a508f966bbaed7a82645e65eadaf24ab5e6d9421392c5b15b7f9b640d34fec512846a3100b80f75ef51064602118c1a77d28d938f6efc22041d60159a518d3de7c4d840c9c68109672d743d299d8d2577ef60c19ab463c716b3fa75fa56f5735349d414a44df12bf0dd44aa3e10822a651ed4cb0eb6f47c9bd0ef14a034a7ac2451e30434d513eb22e68b7587a8de9b4e63a059d05c8b22c7c51e2cfee2d8bef511412e93c859a13726d87c57d1bc4c2e68ab121562f839c3a3d233e87ed63c69b7e57525367753fbebcc2a9805a2802659f5888b2c69115bf865559f10d906c09d048a0d71bfee4b33857393ec2b69e451433496d02c9a7910abb954317720bbde9e69108eafc3e90bad3d5ca4066d7b1e49013fa04e948104a1dd82b12509ecb146e948c54bd8bfb5e6d18127cd1f7a93c3cf9f2d869d5a78878c03fe808a0d799e910be6f26d18db61c485b303631d3568368fc41986d08a95ea6ac0592240c19d7b22416b9c82ae6241e211dd5610d0baaa9823158f9c32b66318f5529491b7eeadcaa71898a63bac9d95f4aa548d5e97568d744fc429104e32edd9c87519892a198a30d333d427739ffb9607b092e910ae37771abf2adb9f63bc058bf58062ad456cb934679795bbdfcdfad5e0f2" + }, + { + "name": "RSABSSA-SHA384-PSS-Deterministic", + "n": "aec4d69addc70b990ea66a5e70603b6fee27aafebd08f2d94cbe1250c556e047a928d635c3f45ee9b66d1bc628a03bac9b7c3f416fe20dabea8f3d7b4bbf7f963be335d2328d67e6c13ee4a8f955e05a3283720d3e1f139c38e43e0338ad058a9495c53377fc35be64d208f89b4aa721bf7f7d3fef837be2a80e0f8adf0bcd1eec5bb040443a2b2792fdca522a7472aed74f31a1ebe1eebc1f408660a0543dfe2a850f106a617ec6685573702eaaa21a5640a5dcaf9b74e397fa3af18a2f1b7c03ba91a6336158de420d63188ee143866ee415735d155b7c2d854d795b7bc236cffd71542df34234221a0413e142d8c61355cc44d45bda94204974557ac2704cd8b593f035a5724b1adf442e78c542cd4414fce6f1298182fb6d8e53cef1adfd2e90e1e4deec52999bdc6c29144e8d52a125232c8c6d75c706ea3cc06841c7bda33568c63a6c03817f722b50fcf898237d788a4400869e44d90a3020923dc646388abcc914315215fcd1bae11b1c751fd52443aac8f601087d8d42737c18a3fa11ecd4131ecae017ae0a14acfc4ef85b83c19fed33cfd1cd629da2c4c09e222b398e18d822f77bb378dea3cb360b605e5aa58b20edc29d000a66bd177c682a17e7eb12a63ef7c2e4183e0d898f3d6bf567ba8ae84f84f1d23bf8b8e261c3729e2fa6d07b832e07cddd1d14f55325c6f924267957121902dc19b3b32948bdead5", + "e": "010001", + "input_msg": "8f3dc6fb8c4a02f4d6352edf0907822c1210a9b32f9bdda4c45a698c80023aa6b59f8cfec5fdbb36331372ebefedae7d", + "sig": "6fef8bf9bc182cd8cf7ce45c7dcf0e6f3e518ae48f06f3c670c649ac737a8b8119a34d51641785be151a697ed7825fdfece82865123445eab03eb4bb91cecf4d6951738495f8481151b62de869658573df4e50a95c17c31b52e154ae26a04067d5ecdc1592c287550bb982a5bb9c30fd53a768cee6baabb3d483e9f1e2da954c7f4cf492fe3944d2fe456c1ecaf0840369e33fb4010e6b44bb1d721840513524d8e9a3519f40d1b81ae34fb7a31ee6b7ed641cb16c2ac999004c2191de0201457523f5a4700dd649267d9286f5c1d193f1454c9f868a57816bf5ff76c838a2eeb616a3fc9976f65d4371deecfbab29362caebdff69c635fe5a2113da4d4d8c24f0b16a0584fa05e80e607c5d9a2f765f1f069f8d4da21f27c2a3b5c984b4ab24899bef46c6d9323df4862fe51ce300fca40fb539c3bb7fe2dcc9409e425f2d3b95e70e9c49c5feb6ecc9d43442c33d50003ee936845892fb8be475647da9a080f5bc7f8a716590b3745c2209fe05b17992830ce15f32c7b22cde755c8a2fe50bd814a0434130b807dc1b7218d4e85342d70695a5d7f29306f25623ad1e8aa08ef71b54b8ee447b5f64e73d09bdd6c3b7ca224058d7c67cc7551e9241688ada12d859cb7646fbd3ed8b34312f3b49d69802f0eaa11bc4211c2f7a29cd5c01ed01a39001c5856fab36228f5ee2f2e1110811872fe7c865c42ed59029c706195d52" + } +] diff --git a/tests/test_data/generic_batched_tokens_v6_go.json b/tests/test_data/generic_batched_tokens_v6_go.json new file mode 100644 index 0000000..ff80e47 --- /dev/null +++ b/tests/test_data/generic_batched_tokens_v6_go.json @@ -0,0 +1,108 @@ +[ + { + "issuance": [ + { + "type": "0001", + "skS": "39b0d04d3732459288fc5edb89bb02c2aa42e06709f201d6c518871d518114910bee3c919bed1bbffe3fc1b87d53240a", + "pkS": "02d45bf522425cdd2227d3f27d245d9d563008829252172d34e48469290c21da1a46d42ca38f7beabdf05c074aee1455bf", + "token_challenge": "0001000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "332897fa11779e9594b096e5cfc5e43b1405338717bf27b49e9956c1f212fcbf", + "blind": "6572ac688f2a37a1e6b9ba7a84a6034d9cb82673723e2a2d1413de60ea296a7ce0ea351b6dfa6e63a1b390460567f903", + "token": "0001332897fa11779e9594b096e5cfc5e43b1405338717bf27b49e9956c1f212fcbf501370b494089dc462802af545e63809581ee6ef57890a12105c28368169514bf260d0792bf7f46c9866a6d37c3032d8714415f87f5f6903d7fb071e253be2f41077848f52b6bdd765ec802dadae854edff52d1677c444fe222146fe425e751c826b4eccf4c4983b840af2f73a07027d" + } + ], + "token_request": "340001f4036e7830ee395c3e0f9d63ea6791db427062f773872ef64bc4a0935e4c95803558c4743f2b8626fbd655b0e2fac742e217", + "token_response": "409401000103ea1ec6b484c65905806dd80e159bee70451ebf99e4e74059da8cb4efc1129292ebaf28a690df8f7f73dc8afe6628c48ead526d157aeb36848554fa0559afa23378163844098eceed1b30d73a6681a387fed082a933bd3597bc1f76a0a77ccf7cb65d20dc14499fb4cb97d883f7b6f5e49ef6e016861fff4a294a620f83d3ce26dd52f6d62cb974d0aa76c23752dc5c24" + }, + { + "issuance": [ + { + "type": "0002", + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "215c56ebf1bce65aff6f99833be25082a9f308f4d39a4cf9b0fd4418a9ca1882", + "blind": "8e023c5f212462a34c03cc59b7316c2f5901bf56695d6e6f05c3f608ec588da686dfd48d200c6953c75b69cda3e19ff48556c23c32dbebc39f72b5fd8cfd8230af807c0dec3eb86b7bbe490767ef6056aeb828b2316fd4b670d087ddde5ace55f6ef2e78b76bb83a79ed462ae150a923d19772a6c8de47e376626c25732e7a66de5ca92a63f93b4dcd7ecd680bb3fec230951df7908c253556af29409dd3c037d5cb16b0861b723d1839090e882463ac10e7cf3a78c12e7664c68aec7a33fde219ab7a0068d32265bfc211476a462c0342092b08f96804e0b83a3ebe93b516db9ddde3d97344a52f3d90302c2e136a39b660f035db44543c15348fd91da04d01", + "salt": "3a0d3ad52a28b49028588d985f491f019217178016c86b45c9a891f68bb82ec6cff8fc68f85fa88f6c16db7c70880f1a", + "token": "0002215c56ebf1bce65aff6f99833be25082a9f308f4d39a4cf9b0fd4418a9ca1882820fc64e2630a0d5166c36696b663ff34518338263383ec92b3347ea30bddae4ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd27082bbead86719e39ee60b224ba39aed396e7b28cfb9cbbd8b654887fc04921f76dc92ef72cb47650cd95bebd277162ab0ea03e60ddf9cced0d8e076ad065e29a04ef8c69fd880a5cf58db7b3f9153ee5a7382685d1b4d1712ee944a92fae4fba32ff4d9288a37f6db1eb29ea7cfc9ea2ccb4aa1c729c0b4b95c5a40e66d9b9f8fee7d87ff8e8513b7ac1e934b74d68b8551e102fbd1fb0404bc1904ada8684aa0afca6275877b33224cd15ad1b771ba9af7821385017ca34e2eb9644a88c204675cbb1bf3b7cd7cdf9055b3b377057faec7a36e81652e6d0654280c017723e878f0c9d993bace51a51685c62ff22f0ea37c244a6478077e914539e65f95372e4cf" + } + ], + "token_request": "4103000208a84566ca40ac42d01bc0e0ad10d2d8f9bf45bb30326ce3a3ad0f2b56589c1e9260679876a1271c83fbb5dfa9433c72a0a6362dbee06f96f3e3591c3ca8dad240e1c9696fa7a41b79306a868cd1cfe66432129ab20902be6a8214d11f8863a74b2ece62de4fc75b4968b87216471e6b719cf6c91c99abb83bc98fabae802aa09868b1bdae58a73023bdb83c54d6bebfabbef837c9d4ef88a7a0e742e32a860e05cb798b4d09d8eb1336da49d8d83fb619ae556617246b624b5a0fd55f16e27150c96b6b4d645d897d04443bfb5f519eabada7b8b4d9013e8f6f954356231dfea1e8e7121eb52a5157fe10381689b7f343cd4380e77727ac96a126f097b5f79758", + "token_response": "410301000212093795bb6bde21df488d56d18ac4efc280d6a493528fb180ff6204ad2c2e6396ad19c348103c7adee2c254bb4ee4f0c0015ac070bf41d23a78856880f1b506ba28ca3d370e262216e68ffdc6be57bcb0e03d7c3e112b63a5695dbbfb220ccdd68a601d829821ffa19b5ddb2da6da3f71e308d70253f43b7b52d2739f2380ad5150be14db16ef39ca9c475255b0879ddee0f1147e9230e12e20b5ca7e2069e9ed87db7507f3eb78d9fbf2a957f8410cc80114e9006d1d6c61d3712b04935d62356b0e7540b10189394eed8206e65631e77e7f75ba0768bdbace3ffae1bc70ca86303c3e0d3268bf4dbe3225a618926cf3f2f4ca2c80a9a2f95bf638699792b8" + }, + { + "issuance": [ + { + "type": "0001", + "skS": "39b0d04d3732459288fc5edb89bb02c2aa42e06709f201d6c518871d518114910bee3c919bed1bbffe3fc1b87d53240a", + "pkS": "02d45bf522425cdd2227d3f27d245d9d563008829252172d34e48469290c21da1a46d42ca38f7beabdf05c074aee1455bf", + "token_challenge": "0001000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "79f68dbc610a490d940367f0de8a008c5f429c535dd758afa59965d7be6d8753", + "blind": "4581461cfd525cd59babb8abbcacafd69c8a6da3fdeffc209be42323d2884c09c15beef67287112bec9f674f6e3ef523", + "token": "000179f68dbc610a490d940367f0de8a008c5f429c535dd758afa59965d7be6d8753501370b494089dc462802af545e63809581ee6ef57890a12105c28368169514bf260d0792bf7f46c9866a6d37c3032d8714415f87f5f6903d7fb071e253be2f4a18606ab08b9d569d144f8944655cda17c6ec74d24a11ebd7622a56d551e223a776fa9650dc2a4a00e083444160e27e5" + }, + { + "type": "0001", + "skS": "39efed331527cc4ddff9722ab5cd35aeafe7c27520b0cfa2eedbdc298dc3b12bc8298afcc46558af1e2eeacc5307d865", + "pkS": "038017e005904c6146b37109d6c2a72b95a183aaa9ed951b8d8fb1ed9033f68033284d175e7df89849475cd67a86bfbf4e", + "token_challenge": "0001000e6973737565722e6578616d706c6500000e6f726967696e2e6578616d706c65", + "nonce": "5d0d763f6e9da102fdb9e1a7f95a1b096391f02ac87f65888233e9415df05c56", + "blind": "fdef8385c0ea23299a8b52b59b23ffa9f1a0f25c3a4c93d95792372910091cd47ec89b14321b3e00a8d3eb0949e7bbd6", + "token": "00015d0d763f6e9da102fdb9e1a7f95a1b096391f02ac87f65888233e9415df05c56c994f7d5cdc2fb970b13d4e8eb6e6d8f9dcdaa65851fb091025dfe134bd5a62a116477bc9e1a205cca95d0c92335ca7a3e71063b2ac020bdd231c66097f12333a13b1c96dbe522e6e94e0ecba8c3abad7fac579bddae7233e8f0ad13192b3c895fc7f4229e9156314a71c338ff74baba" + } + ], + "token_request": "40680001f403f509ce89b5e1012f47b34af3a33a120556a5f9771764c33c8bf330076cfd668fc09d0bd7a740f74852d258cbb1a8b49a000133027cd2c1b2552ae7bf9282c4eed9aaf03a7e99969ad4af29e615be378f9d9440062bdf296f39ec999ab9c7a94f62f9df7a", + "token_response": "41280100010229c3174c22f284bf32ea4f5c2e76b4b969be9e5d8e163861e77eb673ce0c7af96c2745064749fd59edbe9b4b4e7e80e522d72d28882d9b60434ab5c7ed142d8da3465291a04dcb1c3ea1a8444dcb09206a4f3ee92ac498753ec042da0fd72602c679bf7a994f9fbca0fae57740fd47772e237569fdcfa587874f0e1a538abc3333f8ee486809347eef1e49a83381317201000103b027ff354e5b1728d6dae0de169816ad92912d96a057e80363b697e253a0a74aad1513efbeff7356e75c6989863191af7be62c4c0251b441088a2221e96f227322233e1b4e3785ddc98bd3f310f69a51e8268478bc080fac60032c63da2332f230e3f04497959469593e2fac77cd6d58dd3e59081d1279f87ea9cd81c87e38d218093590d6ec8f8caaf4581a9fece56b" + }, + { + "issuance": [ + { + "type": "0002", + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "e7615e63a25a89c13ad49ce0249c8d75ec481dc95e005454563ede8c6b0f1f46", + "blind": "30465e67d38960c99589a56a8b965191aa709b91fd2a56c101120b4453e9a561f2bd041d49ec895684018fa6aba34ab3dd2137777f9e9829a0a0d0a678a06e16a3aed47a7e9652ff99fafb5e86a6807f8ddf5b50fcde11b4bf7dac7ee5e4f200776eba2c33d6eb187ccb109999e8c9c67595b5464f1ed2948b19c04689fe9c29c6863208a2e15b70b0e456ab4f8369569bdb14a903bacde57b6ce0bfa21ada35d52c4fea231b40c26254d5130b41f71a21eadffdc60e41ab3c072236cb2d1926cb21eb873bb66365a72d619a58cfdfa963d38cdf4cb54db1fc2b42731ba207ad9a897092e8bfc9be2a1f4551da3452a22926cc98971da1e2d6287e2c8c013006", + "salt": "625baf9d50c502dbd03c949fe5ff0eda7dd6c23ea76111c0f791b471df045001d557ed76eee8eed94909a4723a683fd3", + "token": "0002e7615e63a25a89c13ad49ce0249c8d75ec481dc95e005454563ede8c6b0f1f46820fc64e2630a0d5166c36696b663ff34518338263383ec92b3347ea30bddae4ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708baa04e4e9511d5e1b49ad181913b67498a06d54eed0bb7ad9a8caa4a1c46dfabc6dc456b1be1fa49d8a630925cb124b3dcf38e4a7fef67bc1cb8b25c4d1f78e341fe7c5f03d7e8ef193602a79d9c11cdb9c758e86d6a2ce5d6bbc6ebab3c1ac426202300a752f9adaf8f0ce3ff60fc7ace0d5a2fdcc29f0161495d8953aa7fd6c27c94ca3609b4981cf07f9e2d00674b5e7ee0701925b7845a7702d9193178594a659eccebfcd12a505bd28c58efdee3dc6b971339dfa24b83f0a24e3197d51218175bf5dcdf7ae6d03a8d274734b40af38680a2bdfd375febd2fb436f00f66c709f89996ad4bd8b28f92b8110036f4df03f3cbf7b8673c37db31f925ea69c15" + }, + { + "type": "0002", + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c6500000e6f726967696e2e6578616d706c65", + "nonce": "d17d88fb4f63d0b3b38d1159bef67ac6d60f649aba512b8e382d497530727577", + "blind": "4e192daf9edea831f3c82a274c5648d758f6930991334d820a2a24e96101023a86de1d18836222c42dcb0eb39a9153ada0b4eea7d3395d55114d8ec972655b47bebb3e66f72f6bfadb5cb976c98907fea985eb13bd5675c9c39144381d0b0038c12f6ff9fd598ab8eaef47a878da4fa10d3bd3e4c7e86a60ce15e07f5de0501c65d653d20f6dd83763f152ec7beecba6ca51914ddf00f79f5143d6854bdefd3032ced824066b8586f6e5d3ecafcb268c99c103cd4fab99eeb3afbe550690172ebe09edb533fec7095c357180439c2c32f8af85ba77b85cba091f3eae542d01a3de3cdabf57439fe6f41fb2d3c15c23a5bc23b3be8fe0140944a76760a7b38301", + "salt": "7e0dec0ad99e373d2fc990821a62962e180bdb72e110bc988f9d633fbca8aa5838fe9582013ca63ca8ba9ef41553d086", + "token": "0002d17d88fb4f63d0b3b38d1159bef67ac6d60f649aba512b8e382d49753072757711e15c91a7c2ad02abd66645802373db1d823bea80f08d452541fb2b62b5898bca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708bb7df188c0ed5642312cb5152735825a75d982d17a705843b9dc59847cae233b3e69a8500978d1fb360794c4223fcf7483e844a1db3e316656456e2d06262e0b051f22e91b4e912cee082b87b40e01a5b70734de93f739e6c0abf05a935a8de4bddecb977bd2dbb27f9680ec61068f8dc09bcce8f82885a1820275e0a049e566a73b8b8f6050d34866281fb58761c7560f0ad32899ad8545d8e633642364155aa6fb397547fb9f1bbfb0bcdfa3401750edc31e6f42cb64a931b87032f51cf22d201d8c477135644044b9003814117e9313cbd45eb76fcd89f5af4200fdacde27fef511279616748b9ddd7fa23e6937047d0ae33ce0572430b9fb793788c1ebbe" + } + ], + "token_request": "4206000208bb3699eb03b73b43d87960140ba6e77a9a18ab6a9492176c420096872a88b15b4bbda37f43bb22ff0e6c730fcda255c4158ce10c8bb66995a67b2fe1992059cf6d686e1e696e8a6375bb6e77d73570cf30fadf94f45f5131550bcf556722c9b62912766b17a828ea69fa3e048d5d46ac70bac8cdd8d86330c719e734874c00fcf23dd25a1294c9610689e59cbf0805b5d9adef2ffa5a9e31902bc6b8dafb39b0aad2c520cbc9c1c55696f9e96a5db2b32dbbd2b4f75c7481499a31ae2df1cc879475bd7ce402ee5dbaf624d87bb7c88f5b243c4094004bb145f71f6d98da7899ff566398468b99e41ce25f1a88ed49fb4ef26d817d6a1520ec0aaea6bf0d93360002089cfb2feafef5b7bf534a945daa929e87756bc8a9972202995b10eebfd476c81fdede8a017513ee84259fcd475330f2379d5d7b578bf181f9df9246fdf4b594c1d989650cde9e040c3aac7c476c2e6c87d20a3381ae0455e21e03b6957670219d7af3a295c932ad7735dbfd43b7c464ca82d293d1910b25c2d6f293af2f46b30396de4f009e0e2ba05f431802b2d4b5fbd8e17a01b587571e06382b5d26895b53327ce2ab9055e0825cf297909f6d3993a516dc9fc69824a7361deaadc3c7a133c129fd93f11906b4b2f88170c80fc5e6596c3ee550da51c1b69835f4f127a71f5d44a94d954fa056c6bc4e5ea1f49f4175eebae188a85a2e680291fd4f441377", + "token_response": "420601000206b0e6922469eb5587c12bacfda4ff18a732049b35fae442431344d136ee8be0c1080b2ad2e0ae379ac39abe3aacdb88b52614ed9c0c1aa3ae10edd9cdd1f776e67c3aeebcc0656cd4c4b109861904b48e9b902f584565125dd97f7ed013b12f5412fbc3378234a904f1621a5b67790f6f677bd6d4682ab5285a342ed4d4a508360ed9b73907451e6690e88313d4f132034a1f76916aeceb43051b5f294303bb03de91c9b12022b1493f3b8019219134d214ab09906dd395e282f255940a411eda4cf0b8ab2121f802b62ce891d84fe3b2c5b32e00fef88a8756ad015ded4a3dcaf45657597f0677e792c250bdfe809edbb567d526e8cd9e9c94ad67c0486139010002baa9791c04572ce81c4b02909cafd1dc248a0ea53b6fa3237b98ddc3fb970bae23733d980846fd7f53a7b90655939678fac43536041bfb99dd4891add3bbd7cd05d363e5142b9917ec0c195724c62d7ae6b7e65738870ad9ccaea0e7415cb268ba21acc141939e4853f9f5e1fab60edad58eee3eabfca317a581534f3666aed5420e984dd74c655f32611315e0af4bb1d37165bd20a55a0c19b1e930516d83d1c2841e382aff16de515cce34cdb1191809e2cc8678b087539e085942be0e50f6e17d81af48c9bc94ffe4f8a6311a8537c1ed911ad88fb861fac00cda3fedfab48fbb073aa05e544acc976ff6368364f5b0c6245e0a7167fb1af485fc90f73729" + }, + { + "issuance": [ + { + "type": "0001", + "skS": "39b0d04d3732459288fc5edb89bb02c2aa42e06709f201d6c518871d518114910bee3c919bed1bbffe3fc1b87d53240a", + "pkS": "02d45bf522425cdd2227d3f27d245d9d563008829252172d34e48469290c21da1a46d42ca38f7beabdf05c074aee1455bf", + "token_challenge": "0001000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "0d399a51a32286661e8ecd6b601cccace4f45e8f41b05d6fae47826d46142139", + "blind": "43dfc280832318de5d86c6626303628b13b62670d8b134ffd008e1226df79dccbeaaf3ac555a9f544db50497757a0a2a", + "token": "00010d399a51a32286661e8ecd6b601cccace4f45e8f41b05d6fae47826d46142139501370b494089dc462802af545e63809581ee6ef57890a12105c28368169514bf260d0792bf7f46c9866a6d37c3032d8714415f87f5f6903d7fb071e253be2f429a429ebcef6ada26a201fdbc171f7c2739590085581237586e468660f6fdeec5ecd5e39cb3c908d1312a8baf8484450" + }, + { + "type": "0002", + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "1a89803678132b4328084be9547b3e1ab0b88ebc7b9e84acca47947dbc6b14a7", + "blind": "1c761ba8872e29390be3a2c1328b4f9ccc0012be0c2b09bf558ac0b334f84d0646ab2f89cca0c65f859a78b32b2407a90eec56f54c55916bdf5ad0dbaf5064e5899e18b282e10940f73917771058a0b1c992f679b3ed4f9bb8a2d6a6561e85a86e7a78a6d1345bdbf8a02d58ab92bc458852e8c961d90a14cae721728fd221737f711ae28b49c4b4eb6b76f8b58541630bf78f6fe351c98da801d246f8b54787046610f136489cc8ca28dad8377983c3fc2e28e7ae09365b7516f1cba21b7a0d9ff87608c5cd91fbec89f978053512083bfb0adf22c74f0a96256e26c6d9f9436469972580e60a1104c9672b66891092417870d82eae4fab3c76caca50eaf5ac", + "salt": "f70daa7809b3667c616b2666ce30ad74b646afbade9fd06424d433f5a8b395bf669fb609b845055b4e4a133712beb251", + "token": "00021a89803678132b4328084be9547b3e1ab0b88ebc7b9e84acca47947dbc6b14a7820fc64e2630a0d5166c36696b663ff34518338263383ec92b3347ea30bddae4ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708ad0995b4e273a9c7d825c7b2d7293a42f712216925639a276b1a5c358120f6afb5a28c6319647b47ffef79dc39ba0eeb42f4cf22bcbbad7d774a5df9904f80c0826f12b8542291c53c20f6c6f05bed4146c2b09a2b5e424d4622ba40531b84d026ab202d01940c154ab47c6a9b377f284462bea8a6285383efac6ea8b302e4bcb8855d133396fa2a5532a1dc3f280a101770d8f2e5d8142d7c573c9f9e29f160624b7c95811687f678a7994295695c14b1899c06f60e6a0fa04cfc6b8184e59f1f40babbf0dbbc3cb91047a659723c800b574cadc0491ea40f15e45a0337a3e7cd109de3b4e3517d4637059a9ab3c4a218e052d15479116d3c437e4962ce07c3" + } + ], + "token_request": "41370001f4021addfdbb3234410241c073cb66734cac677334930d7741cb973731f19b0e500e80dc6619d11c754b9f8909da8d2c3ca600020849309ffde4f288fa5fd2e32c68aad9630adabbd51f273814d7583a27136527e8d77fa89c8798e945b833ca9d7ef20233e55de142519891fea24222122feef56c7e3f7319f012e77e247b2ba1c9ae501196a82f8e3bb3039513c79f77bb0014e1666ca09113565bb88dd65729e8377dadba084c3adae744b7f0b6e7a66f7715c4677bfe364cf08f808eef5c607fe716a8509b00b688bb874e90badd7781c9f3e32d9178070103874ddbb94f7f8d44893ea97c869334f9ec54ad421fdedcbe264d4732da0b54ad25901895248df6cc75a70e9fc06fa1f7c9bfecabd45eec42e22239eb76f1765a1a5de3bcf12b5e4d8737ff8b54f911778038fd74fb8b3e4a3e3d", + "token_response": "419701000103f46a70859a87727ade7ee8534c42b676c28e22fa906a669f03172331cc85e1157097214f482a0744fa9d5083a85db2b208e4c03bd4c9540fd5f183ec590aaeefafc849ff51c169149b653af450b53b830f587c85bdf500074467db6bcd155b6657ad915e4e94d28642173be93a8c6c99a3ee2ee9ee46686f01d5f9b8208593240cf7db7a9cde0c79ce818720313344470100029e215a04d99389aa9ed15f2df507d9fcb88819b989705fcb779d6af8bff5d5c563190ba2267c58c49a923cd4d80277eb72d287bb224771bf2dd245018195a8eec6a6ee454e7f7600fa2f974a2feb8638620702332225288776a8b183bb30f251194df955018bdb31cf992b8e37e8dcfee22a2dab536ed0a3c918cb74b0568b100df7ca97bb15b6641819d0db806ab9bb67b295a3ecaf1cd07576598166051ddfed98d6d430b176081e41432a4607213b1291ef2becaaa337961139bee6a57e0aa8b42d192bf468f4d187e72f7cedc6393720c34030fa809c62dd9b4ef79aaf7af950a284c9df77cfcd17f6f5eb022963ffa1d57d4394d24f3204db260dad97ce" + } +] diff --git a/tests/test_data/generic_batched_tokens_v6_rs.json b/tests/test_data/generic_batched_tokens_v6_rs.json new file mode 100644 index 0000000..0a5188b --- /dev/null +++ b/tests/test_data/generic_batched_tokens_v6_rs.json @@ -0,0 +1,54 @@ +[ + { + "issuance": [ + { + "token_type": "0001", + "skS": "9e19dad32d440b14a7674143e272c830dfb4d5de61eae63c76571570956a88bbe66577db033d3cf043d343208436d6da", + "pkS": "028ff446ea2e2fbd761a6ef7b1137bd59a760d6c65f3b82784cefc5843f9b9ed65cdcaaa90b293f4b0cb818a873ca17d15", + "token_challenge": "0001000b497373756572204e616d6520941de0bab199d9a3cd01d5f58529d5cc9b1ceac6614d92ff75a36241c96439b20005612c622c63", + "nonce": "b771dc02997c71738e631f7f52e1f786e9c80eb51e9ed54a1302fbf98f5b32c3", + "blind": "ccb1a833c28efff813ec2de76d4bd325874da893e8116d583ad3c648341398560a6a2c1b64c52b4ea9ea97b4e1f24e54", + "salt": null, + "token": "0001b771dc02997c71738e631f7f52e1f786e9c80eb51e9ed54a1302fbf98f5b32c3c605cc17c7449af46256832938618c13b44148d3ba19c135c0e992dc15463a626b2e907957aaa5def3d775fa880539bed74e2c2f8fb63f80b64624844b9053737a28749068a0a33347af11be755343eb7e23bd1fb6b18539ae4ce776ead2c340e8ffc496583950c2672683568f78423b" + }, + { + "token_type": "0002", + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945764149424144414e42676b71686b6947397730424151454641415343424b59776767536941674541416f4942415144312f78627745396c476a354a4c0a6736326e583844795035634864515739396531465a6d30673879724d51357742474b4746555a4f554246464f706c45774d4e726763764f6a6e55733351346e4c0a775a6932624530676235564c7a6754756b587a2f3243764742534661557374466176755855475777702f516e325a4f51316d58634562376257477463494539790a512f356e5962493934654e596b5071614635594a3871446a333447702f6865706a4a73586a45377154323631626159722f5839496e306b6441366c397542384c0a5276745a614e5677546d4445686e61625a7a7154416c517038763379424558736f63456d496d35336443634c6347494d6250387046747559624a6b75546e54480a366c3863446c2f586e764661535a5148464c516a6f793350764c426b3135706f5466526266306a2b6c4370537031353964673469522b756a625376344b6a48520a33733979394f563141674d42414145436767454161564134364c554771742b694f50503572784d65546c654d392b4e5166762b2f415a77564569736f596d52370a63446930525a676a2f78564f486361304d565a70446667496544354a796a482b317831646e4a4f472f57446e344a6b6c6f57446c7937346e46314a7738664e4b0a464d44315474566267364b5473302f426a68744c5a6b69756c63636e787a774a35786857334c6d466a6c716263766f32587976447a79527858727a38444f4c6c0a4b7a6c6a4d7169624d6f7953574d2b2f497a4d6f734c517a336c30674367424c49334e587559324c736e6f4b38426a314a484a3569674c3278622f456a4a796b0a59742f4b676b4b5167574e4a596252564646467035494139656c63515131667a66776a2f417276734a3858414c464943326c657343705a594d47776276455a410a54706774422f61576361534e484443732b4b724d6c716b6a6c35622b62584730344531515445374641514b42675144395773783233416e6e5531364f6f5a646a0a4d684137495361356b6434684d4d704c38454d4c4a74556947594c74747163695549534d48497a4f6563427a734e6f4a39447144474233766c6a4d6e514e73450a7266536c75426e3472747161594538696b3459376b55474e7771552b4e4f726a676e66586a676945624e712b4f363146474f304942376c594c41766e303736740a692f796e586254422b545a342b696e3778495952754e314151514b42675144346b4a3976726b5273756153474e5879497256706e484533466b444641346a4d410a384773354f7248782f48556a6d5547424863354f6e6568717975474b68415251556d64534c72574e3863785236504f684d576a76775730694c4d514572552f740a4c6b4f6e37636f2f6f39584c3165784f556273666c7a7666484c625375574a484c593943656a6e32484d523549417531626555684a3436384e4137594778516c0a45416c64447743594e514b426744785a63356a67677939714b587a364f76427a445843345464776f5265784d44665157304447493133346536355171545265630a62475a667170374957374a7a4c777a74387a346b385953506c3748432f696a594d7732436c4255727559444b62693944537464486d34446b47363538746e4c700a4d686a546957335a37435569572f79346d6b57756156543663502b513762757a4f385332536f646d454c61796947756a665867644f2f3742416f4741546c54640a4f636530586f4d655777454f447442366c6458776d4672356570594f4e72534e5263712b657944784656666473622f66335279634f744e57776c30614d756e560a443677536f506e625274446676694f68437a56736e414635674850527056415947427179797431707163507670547253656f4568614131586641375a3773356e0a7743356568375848494e6939643935362f376a456e594457554c6f763367497777546653477955436759422b6b77575a474e59707577584e76486a42646a4a6e0a62455a2f665952712f735278772b52556347546b665438566e355850614c457032726149642b52674c6c65777345704f77447753624a7868626a315051324f490a62744f716e476d31362b41434d556f7457343477524c723954694844793059444a7154734577326a4d34596c726b63386e6e77335748596a4e2b74474d7a646d0a687244433772415047576a46383439645476776546773d3d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100f5ff16f013d9468f924b83ada75fc0f23f97077505bdf5ed45666d20f32acc439c0118a18551939404514ea6513030dae072f3a39d4b374389cbc198b66c4d206f954bce04ee917cffd82bc605215a52cb456afb975065b0a7f427d99390d665dc11bedb586b5c204f7243fe6761b23de1e35890fa9a179609f2a0e3df81a9fe17a98c9b178c4eea4f6eb56da62bfd7f489f491d03a97db81f0b46fb5968d5704e60c486769b673a93025429f2fdf20445eca1c126226e7774270b70620c6cff2916db986c992e4e74c7ea5f1c0e5fd79ef15a49940714b423a32dcfbcb064d79a684df45b7f48fe942a52a75e7d760e2247eba36d2bf82a31d1decf72f4e5750203010001", + "token_challenge": "0002000b497373756572204e616d652011510a693c9a5af2115e1d93646ec55d3696ce6627d8f595be05f0db23a7d5e40005612c622c63", + "nonce": "af2dc5bf9e3a6d3b4a625bac32e19061774901c937d97f9a48df381cd372c3a7", + "blind": "de888a141ab0adf0ff07e4cc929b6ac1a599e65c3ee6f4e2c602b0fcb47a08712c7f2ef6f3fc0fffd6572c66498cf044aaceac28c56dfba5faf36b5c74bb40aa76aba559f44203816512bcedee9d168e2bcca80c889fd74af02fe24c22eee7014443bbf4d304e47c9686db966aebbb5543a07b37036ed03d844202de9c3ee59c8a1ebea14c1e572f2aaed214f831e1faa269854ccff13e62f0f144dffc9b2e982b7632d8977f815f294aed7a5f544b038f7fbbe33fec777586174ceda91c7579da859563da8272fbd48dcb665c4342fb17249bf87846534cd60e224cec187f02e14b91bb68bce3a4b7c135286f104916dd2cf9dcea44797fd7057073a0cc69bd", + "salt": "6b94c56af3bb58084c07e08b3e7a848132fe707d09eafe25003f7a26c8535b04a7526f12ca1eb264d4b6e4015309196d", + "token": "0002af2dc5bf9e3a6d3b4a625bac32e19061774901c937d97f9a48df381cd372c3a726667f6916c5aea78f184c27002c95dd4ed283189a68224ad7f322ef706a3b58c19b7457b57865de3c54bd8b1860fed5ab4c868bbb324145f17f74e77c77be9538216d8140ca0c87bf4cd17077f1a82e6aedd2255051191f5006f10b06ec8db240ebfc98fd80a9c1bea8f3c00d38902c2cde357c40f098f0705605002afb9e80eea058602e5fc86b272b9bdac0d68c8034751dee5ef2c7e4ed5424e66b52ad16ba78b1153410ce94207b91646079b25df936c577d747aec615797bd9e96ba8dea30c7c3dbe4aa0f1b687fb865c36f197cb19df9f7c2457874185b869cd7428af0227baa64fe98e09ceb4a4f76bba48f67064161bd13c5390658eb0a1a135de3b27c408402aaf280d54a438d51756ec2585b5da29fcb1c8a005a747414874276c92996ec414a8ac4eff1c57cfea3b9010781d9aec15a32f1307f557581c6bf507" + } + ], + "token_request": "4137000173039e6793ea9ad36c05b19247a450f78c9a6598f069579102c98723c534779461e54b5220d83d2c4f975a40d0a77de8498c000295a16b1f1b469884a38ef1c7e691ad0355ee59bb4ad462934dc15dac6294a57030fe83677e31c806c07ed9ca3450a26de0607c4596509a5c6128ff1e788f9b0fae40a130a0e022e5026fc34c44d6223525dfe181a603bed2ee58a91718e4550e65396d721768605adb7b82765ffd1769f1a3d2d5d62160025bc7c16803d0af81f969359160e11cc1862dcc2ad643a055daf7c94f9a5a351dce3e2f1da19b1d3270fa6b72d3f568952978404454a9022fa1d078478073a7615ffa6ebeda95621c14075986715e0b25ecee559b0e78e0074b297632f536faca4fba86e6f7d6062520dfa47283996ff2a297be0eea01cf4c37b575f64d9a65a5140f40d37a8223bc4c", + "token_response": "419701000102960715043dcddc055f5805a41e2fc119ee58cd8d0fd057fb9dd9d6b6adc97f5d3b6c46fe36cf6fdcbc73c6aa049a04bdc6d824fc505b2683b9d1b023324fb3a87282fbb7d8ac5f95611426bb1e9d37909b6304e19743c4c5276aa6c8182bc9f5b7ab71f31aa75bd0aa587f16ca093ab1ba1f7a170e1d16df59a598bb64e6a97fd8cca72a87ed8b2eaea501c7a1ef5ac001000297cfa38a3fab7aff07a6f1ad9c022692309c76aa657149afdac4cc4dce735b415ea30c9ba4b04f3320542e2c4f5af190af826ae264b6cf3abdd0a559cf8868ed5490ff9d21e04a8ae12eaa95adf28293e1b27425e02e2539caebf5849b281b2475084bdaa2bf368d7cb0e6126092540aa5511db0f507249be6af93e4c2745db6d6689eac80e499e2c54e392e17be76a4c3441e66bb5ba608a5b32f102ad434c691e82828946d1dd3587f68acde191f4d7933f3f5649ef5255e4f8b6a1fddc85974603e5c5ff87a9f6209f1aeb7b01919e751da5de3341ba8044f5419fb2fb6125b08a27ac0185a976d82ecb5fd6cc0508f96ff8290939e869641907cc8b60186" + }, + { + "issuance": [ + { + "token_type": "0002", + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945766749424144414e42676b71686b6947397730424151454641415343424b67776767536b41674541416f4942415143794231723031627043727969360a4238596c68415a544c51306c762f79416d4d4b346d6974445845744f654a7a6d4244556630324c696d775573477472637a6270367541466551796e49504134300a3642774859482b3254674f375745426b694b563776456e71673471727a7356785a7572337255496850475a717a654b47797035747651617741624b65436654590a5546485850346758696f587a556e39634a71694e707756343245475133584738736f716d4b777a5273665a7256474f7a4631734a55425964332f71365a327a660a6f5a64514f34325343544d587862346558344e536771535935593747494357767457364e6c71412b7132596b784a2b3855534f743941703477474a73355030360a717677564a4a7378574378556e6f6a66547845643948795948554a4d614e416c58344b6a79626b61513250496865765a4a473035694d4549627457694875596e0a3534577a376f4d4441674d424141454367674542414c42356c424e686a697175515676766574385465635758776847717435327555452f3447327a426e3744670a355038714b704d71307a6f3457793832533842586530524e424b597851386a47703371516e384138537a306e4a704865304b34587570726d7367727a624649560a50536e46304b50474c75694e584355426845304471337179783139507634546a37373943463236632f73582f5066744f7977744a4577716742302b635a356f320a454e48395a5176634d4e653869322b376a45775262463654306e6e4f752b735143343547495661634b594c41705a4c74305253343336486646314155505249660a2b4c794a323858555347494953496b716e753673797866444551534534556a7a355371674135646d58654c6f5076624369454a3270742b466d504b5649626c370a32776754592b496d37784b314551396432735a4666544f6a7437646e596954736f47626c376151657a3545436759454136484a434966306b2b43616b454c74330a4a724364334a4164787a674d2b7637764569624c4441542f5562792b674f6f454b3835613244624a4f6f474b66673276342f525837694c4e414c453635774d540a4774675077704e685431654451444c56486e7a715a6245495a703361745239386e596f4876634f7875716b36473379516d427754557662304e306936482f2f410a7661354363744745384e536c7858313869494c5676754f773569734367594541784246384b4e566e316746634172356c71306a537276686477385868696e45500a624134754e6a692b6654354673624961594745613079785877685a3167354136687764726932326f2b59544b696d64697a7a7647336850455a4232534464572b0a384272325476393076635133417a46634165435246347979795663367a674d714779723534695262522f6a65322b36793353775a625a7a5a76674956476a4c420a4674696863377a6c416f6b4367594179344138706355614f4b61627649706a4a6c773532482f546b6a5a674a74354b4c336f48436437574b4a6b3172326378660a36694a6f4146573677472b586431647742534c5a74445136577041527a394a3270614f4e617352356f5358512f3038324252456f674c3764397437506c62796f0a6e6e566a53316d7a435362327253545970677830744368766b544e726e59614a2f5948397232337861426d41534237686f674532626f396855514b42674668700a53674e6674364c596d685469706858476b4c6f315a4a4b532b46446c436e382f4a626474667667617a484b45545543496951415154734a47506c6446416839420a5355316c3958774f56515a7a2b43706157694a70353354396f49353867387a6857342f6a775643513266326d6c456145624f69686d54305243494d3943672b570a495866487a5346334a34636b36364b76384164745977493744696a7947662b6a54367051307a4f42416f4742414f474a70612b783237382f68527a5948414e6c0a5542426e4c755679453743797433396d632f6a36564a7766765a2f6173314e5133664a615551546330334b6347467363354c6338567a73567a336e69436a324d0a6431574356354c6a6c377763302f6743423863794774316555774f713258556d4c6d73365a53434f694b6c674a662f364178635a65764f316543344c46726e4b0a397a597955646e476a716d59436d4d3334756643455670370a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100b2075af4d5ba42af28ba07c6258406532d0d25bffc8098c2b89a2b435c4b4e789ce604351fd362e29b052c1adadccdba7ab8015e4329c83c0e34e81c07607fb64e03bb58406488a57bbc49ea838aabcec57166eaf7ad42213c666acde286ca9e6dbd06b001b29e09f4d85051d73f88178a85f3527f5c26a88da70578d84190dd71bcb28aa62b0cd1b1f66b5463b3175b0950161ddffaba676cdfa197503b8d92093317c5be1e5f835282a498e58ec62025afb56e8d96a03eab6624c49fbc5123adf40a78c0626ce4fd3aaafc15249b31582c549e88df4f111df47c981d424c68d0255f82a3c9b91a4363c885ebd9246d3988c1086ed5a21ee627e785b3ee83030203010001", + "token_challenge": "0002000b497373756572204e616d65000005612c622c63", + "nonce": "d0e02f3aab17957e678918b88bd107d001146822edecfa9286827f8cd2e887d0", + "blind": "8fbb53b0b61268491d30a0b56b8184746e211031462a112a4259cc29c948bbf9ea2bc5c1b395185e2f9f422235d55f37f4334f080d947a66e47e9e842b4e663cbf7b5d2e648c8e987db8b3ffba9ef991b7e4b25d1c2e3b1726226244690237d4c1599d50efac6571f7173a67f4c93d07ca5b91ceb31884ede2475f3fcd52eb72cb38ddf18e37563b8d4b1f1397f05df3e6d65bdc7f909f430208898463774230ab47124a987c776e8224ae9b835889e88ab306f1ef397e0538ab93f681e563a83432b120f4dcd336bfea0978fd6d1aa06dfe635a0a1c1729ced586054d304af479c408e564d50473d86d0c84a1c55ac1eefda6347898722e5b60e02d56a495dd", + "salt": "c8e1c6d9d2911d432872eed71b06bb197d29fc22d675bd348193bc1440a6b3d798943fa0b77b12f2efad68c08cffdc64", + "token": "0002d0e02f3aab17957e678918b88bd107d001146822edecfa9286827f8cd2e887d0201c8af95ce37c05aaa2acadc50fd6d8a825e992c5bebf2d617f0359cbb669d85847ee9ca05f3abc8de756db4181bb525ab806888d71f3313c37d63d50b05e2aa479db91b2e7175b49cc8c580bdf48541c252ee23754b999ccd309e3d51740be0da7f346689617a8421d47d5803b1b9e38dadbf6dd377f8553d86d5362124d154457f4318ecf67346cd553c684b3c4e56b20083e195c0f944c7fbd941dba0e0946d03d9750ce0be2470a9f3abc68185c93bc2e6dc2bcf84d61a50f96e9b229ef7308aac70b86e61c5bbfcff34eb2c23cfaab41c8775c38d6f095d56d4e43f3af8c8919dd6d11964038aac6245c7c7914a3e6c171cf262d67cf021f6c37621a6ca4f300d9141141ce8bc484dfbdc8d57572554b4424021014f7a07f5eeb9ef8c9827abd018f257cf1ac749e5d35dfcadab80abb9e2aaeaa76d4f1c2dfdd5ff7f5" + }, + { + "token_type": "0001", + "skS": "79d090708ee7b744f1a764a345cf5ed372b0d12dbb02b8ede06dd649712e1091af235466040b350d5fd547ab3b23aad7", + "pkS": "02e26e2214b4307c84c9bcce7cf89d90a65ac201d5c6972f59e66afd3e4a543addfadd364bf8d5a503f66e3bfbc358138b", + "token_challenge": "0001000b497373756572204e616d6520f367e2f0d1744a7ebb63ec0e7d80be9d32314ed9c4d163894debca99cd02fcc30005612c622c63", + "nonce": "4e39a695d77cb30da3dd7853fab3d8ad903aed8507559a158df4b05510dd6db7", + "blind": "1b48f7d21eae4826dc45097a7265186cc3d5c809889fed2ae498dba33c34a630106f9551ae7ccd0471a1d89ed7c7c243", + "salt": null, + "token": "00014e39a695d77cb30da3dd7853fab3d8ad903aed8507559a158df4b05510dd6db7a4daeb7e44acdc36c1556bb646f452343e6f33bf6c61340efe89e94d0026f245cc5ac4950d730cea0a8fef3f63d769d002fd4655e786156b2ea5a99e26aa0694a1b0b9bbff925e2fc7cc82fdbff60bddc4b4e0b0776d2a0f4085c7c914eb310891b1bda47deb869861c4f23de498a6ef" + } + ], + "token_request": "413700022a1b4a45b6c832014f518cbf671f5069a9bffa7c98ed82d9df888a470cb60ef6f8951433a9d515304513313996bb5693f23d9be93e56ac59df72d7644604e316c6e779d178e91de907935b762f08a2516fcd2b32c5ec305bd96a44e420660d9fa6d1ed89050fcc538ca3a4afba2e1cb49c611b74918038c2231a4a0e85f7173260f31cd1ed6a0f3a3ec5c40d58833fb09e55c6191b7c0a3ed061dcc7d78ce972f7184bb8181010a650b36bba472938c8190b9cc2bc3df8436a425723d6230aa91d397ffd680e6ab12ea7586ca168c43790ece3d9eaac1a03a98132b70008eb6a9afcdb41a15a21fc59f5efff31e17b26c6c884a3f21f26ec8e46813753439fbb0a000194034f03b6b499800ef6c4ff5ae4e3ba6c71c8757e65c1f2533e84fbf1c04dc102100ee8717427f6833347f467311ea53acb", + "token_response": "4197010002198be0563250be6ba529e2b1acc30a3c4564019c227856609344adbd574be40d0b5ee9f0ba071711e6f10075ed83a3c530342ebcc7fd54a4421b93fd98e9cd61c27128bbceff0d1eeffd2e59523c7f101864ca97e2f889189263feb351499c0319a5a612d74a2650772c58e109ff7db789a9f10ef1ca409fa4541658b6e70128cc9ed23d0390b5ea529aeb4f017f099b6f0768a10a0d021abe578b85a289d22928ab51b4c07da71016a6a2e49c800a4dc0bc767ec2b02a1eba7baa94efcd582215a07201f5b1b9c5cade04bbeed6e82abf8235003bfb16f8d346112c3f865a4d6398571a5e6da96d6d810a6016433c258bb261c6da3824142f1b283a62038db901000102cd0c4e0dda07218612009f7402d1d4230449ef45e01a9d5a11424d49e3828d47331ad9a24ea960b3653e8a00f0b4ffaadc12d787feba11edfbb1dc171fe391f8a3c1980125f5c909fb3e1bdcc34b65d3b68261c0cd5e0fd521035173e0c6043701697c03cd73fab74694f53aaed84791a58ec78add1c7b754b4a5d25a7dfab21b27942dcfc732a4457dbdcede8a89886" + } +] diff --git a/tests/test_data/priv_verif_rfc9578.go.json b/tests/test_data/priv_verif_rfc9578.go.json new file mode 100644 index 0000000..def87b3 --- /dev/null +++ b/tests/test_data/priv_verif_rfc9578.go.json @@ -0,0 +1,52 @@ +[ + { + "skS": "39b0d04d3732459288fc5edb89bb02c2aa42e06709f201d6c518871d518114910bee3c919bed1bbffe3fc1b87d53240a", + "pkS": "02d45bf522425cdd2227d3f27d245d9d563008829252172d34e48469290c21da1a46d42ca38f7beabdf05c074aee1455bf", + "token_challenge": "0001000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b3000e6f726967696e2e6578616d706c65", + "nonce": "6aa422c41b59d3e44a136dd439df2454e3587ee5f3697798cdc05fafe73073b8", + "blind": "8e7fd80970b8a00b0931b801a2e22d9903d83bd5597c6a4dc1496ed2b17ef820445ef3bd223f3ab2c4f54c5d1c956909", + "token_request": "0001f4030ab3e23181d1e213f24315f5775983c678ce22eff9427610832ab3900f2cd12d6829a07ec8a6813cf0b5b886f4cc4979", + "token_response": "036bb3c5c397d88c3527cf9f08f1fe63687b867e85c930c49ee2c222408d4903722a19ff272ac97e3725b947c972784ebfe86eb9ea54336e4334ea9660212c0c85fbadfbf491a1ce2446fc3379337fccd45c1059b2bc760110ee1ec227d8e01c9f482c00c47ffa0dbe2fb58c32dde2b1dbe69fff920a528e68dd9b3c2483848e57c30542b8984fa6bfecd6d71d54d53eda", + "token": "00016aa422c41b59d3e44a136dd439df2454e3587ee5f3697798cdc05fafe73073b8501370b494089dc462802af545e63809581ee6ef57890a12105c28368169514bf260d0792bf7f46c9866a6d37c3032d8714415f87f5f6903d7fb071e253be2f4e0a835d76528b8444f73789ee7dc90715b01c17902fd87375c00a7a9d3d92540437f470773be20f71e721da3af40edeb" + }, + { + "skS": "39efed331527cc4ddff9722ab5cd35aeafe7c27520b0cfa2eedbdc298dc3b12bc8298afcc46558af1e2eeacc5307d865", + "pkS": "038017e005904c6146b37109d6c2a72b95a183aaa9ed951b8d8fb1ed9033f68033284d175e7df89849475cd67a86bfbf4e", + "token_challenge": "0001000e6973737565722e6578616d706c6500000e6f726967696e2e6578616d706c65", + "nonce": "7617bc802cfdb5d74722ef7418bdbb4f2c88403820e55fe7ec07d3190c29d665", + "blind": "6492ee50072fa18d035d69c4246362dffe2621afb95a10c033bb0109e0f705b0437c425553272e0aa5266ec379e7015e", + "token_request": "000133033a5fe04a39da1bbfb68ccdeecd1917474dd525462e5a90a6ba53b42aaa1486fe443a2e1c7f3fd5ff028a1c7cf1aeac5d", + "token_response": "023bf8cd624880d669c5cc6c88b056355c6e8e1bcbf3746cfb9ab9248a4c056f23a4876ef998a8b6b281d50f852c6fa868fc4fa135c79ccb5fbdf8bf3c926e10c7c12f934a887d86da4a4e5be70f5a169aa75720887bb69053692a8f11f9cda7a72f281e4e3568e848225367946c70db09e718e3cba16193987bc10bede3ef54c4d036c17cd4015bb113be60d7aa927e0d", + "token": "00017617bc802cfdb5d74722ef7418bdbb4f2c88403820e55fe7ec07d3190c29d665c994f7d5cdc2fb970b13d4e8eb6e6d8f9dcdaa65851fb091025dfe134bd5a62a116477bc9e1a205cca95d0c92335ca7a3e71063b2ac020bdd231c66097f12333ef438d00801bca5ace0fab8eb483dc04cd62578b95b5652921cd2698c45ea74f6c8827b4e19f01140fa5bd039866f562" + }, + { + "skS": "2b7709595b62b784f14946ae828f65e6caeba6eefe732c86e9ae50e818c055b3d7ca3a5f2beecaa859a62ff7199d35cc", + "pkS": "03a0de1bf3fd0a73384283b648884ba9fa5dee190f9d7ad4292c2fd49d8b4d64db674059df67f5bd7e626475c78934ae8d", + "token_challenge": "0001000e6973737565722e6578616d706c65000017666f6f2e6578616d706c652c6261722e6578616d706c65", + "nonce": "87499b5930918d2d83ecebf92d25ca0722aa11b80dbbfd950537c28aa7d3a9df", + "blind": "1f659584626ba15f44f3d887b2e5fe4c27315b185dfbfaea4253ebba30610c4d9b73c78714c142360e85a00942c0fcff", + "token_request": "0001c8024610a9f3aac21090f3079d6809437a2b94b4403c7e645f849bc6c505dade154c258c8ecd4d2bdcf574daca65db671908", + "token_response": "03c2ab925d03e7793b4a4df6eb505210139f620359e1424491b8143c06a3e5298b25b662c33256411be7277233e1a34570f7a4d142d931e4b5ff8829e27aaf7eb2cc7f9ab655477d71c01d5da5aef44dd076b5820b4710ef025a9e6c6b50a95af6105c5987c1b834d615008cf6370556ed00c6671e69776c09a92b5ac84804750dd867c78817bdf69f1443002b18ae7a52", + "token": "000187499b5930918d2d83ecebf92d25ca0722aa11b80dbbfd950537c28aa7d3a9df1949fd455872478ba87e2e6c513c3261cddbe57220581245e4c9c911dd1c0bb865785bff8f3cfe08cccbb3a7b8e41d23a172871be4828cc54582d87bc7cfc5c8bcedc1868ebc845b000c317ed75312274a42b10be6db23bd8a168fd2f021c23925d72c4d14cd7588c03845da0d41a326" + }, + { + "skS": "22e237b7b983d77474e4495aff2fc1e10422b1d955192e0fbf2b7b618fba625fcb94b599da9113da49c495a48fbf7f7f", + "pkS": "028cd68715caa20d19b2b20d017d6a0a42b9f2b0a47db65e5e763e23744fe14d74e374bbc93a2ec3970eb53c8aa765ee21", + "token_challenge": "0001000e6973737565722e6578616d706c65000000", + "nonce": "02f0a206752d555a24924f2da5942a1bb4cb2d83ff473aa8b2bc3a89e820cd43", + "blind": "af91d1dbcf6b46baecde70eb305b8fe75629199cca19c7f9344b8607b90def27bc53e0345ade32c9fd0a1efda056d1c0", + "token_request": "0001a503632ebb003ed15b6de4557c047c7f81a586881433312ad3ad7f9416f2dfc940d3f439ad1e8cd677d94ae7c05bc958d134", + "token_response": "032018bc3f180d9650e27f72de76a90b47e336ae9cb058548d851c7046fa0875d96346c15cb39d8083cc6fb57216544c6a815c37d792769e129c0513ce2034c3286cb212548f4aed1b0f71b28e219a71874a93e53ab2f47328271d1e9cbefc197a4f599a6825051fa1c6e55450042f04182b86c9cf12477a9f16849396c051fa27012e81a86e6c4a9204a063f1e1722dd7", + "token": "000102f0a206752d555a24924f2da5942a1bb4cb2d83ff473aa8b2bc3a89e820cd43085cb06952044c7655b412ab7d484c97b97c48c79c568140b8d49a02ca47a9cfb0a5cfb861290c4dbd8fd9b60ee9b1a1a54cf47c98531fe253f1ed6d875de5a58f42db12b540b0d11bc5d6b42e6d17c2b73e98631e54d40fd2901ebec4268668535b03cbf76f7f15a29d623a64cab0c4" + }, + { + "skS": "46f3d4f562002b85ffcfdb4d06835fb9b2e24372861ecaa11357fd1f29f9ed26e44715549ccedeb39257f095110f0159", + "pkS": "02fbe9da0b7cabe3ec51c36c8487b10909142b59af030c728a5e87bb3b30f54c06415d22e03d9212bd3d9a17d5520d4d0f", + "token_challenge": "0001000e6973737565722e6578616d706c65205de58a52fcdaef25ca3f65448d04e040fb1924e8264acfccfc6c5ad451d582b30000", + "nonce": "9ee54942d8a1604452a76856b1bfaf1cd608e1e3fa38acfd9f13e84483c90e89", + "blind": "76e0938e824b6cda6c163ff55d0298d539e222ed3984f4e31bbb654a8c59671d4e0a7e264ca758cd0f4b533e0f60c5aa", + "token_request": "0001e10202bc92ac516c867f39399d71976018db52fcab5403f8534a65677ba9e1e7d9b1d01767d137884c86cf5fe698c2f5d8e9", + "token_response": "0322ea3856a71533796393229b33d33c02cd714e40d5aa4e071f056276f32f89c09947eca8ff119d940d9d57c2fcbd83d2da494ddeb37dc1f678e5661a8e7bcc96b3477eb89d708b0ce10e0ea1b5ce0001f9332f743c0cc3d4748233fea6d3152fae7844821268eb96ba491f60b1a3a848849310a39e9ef59121669aa5d5dbb4b4deb532d2f907a01c5b39efaf23985080", + "token": "00019ee54942d8a1604452a76856b1bfaf1cd608e1e3fa38acfd9f13e84483c90e89d4380df12a1727f4e2ca1ee0d7abea0d0fb1e9506507a4dd618f9b87e79f9f3521a7c9134d6722925bf622a994041cdb1b082cdf1309af32f0ce00ca1dab63e1b603747a8a5c3b46c7c2853de5ec7af8cac7cf3e089cecdc9ed3ff05cd24504fe4f6c52d24ac901471267d8b63b61e6b" + } +] \ No newline at end of file diff --git a/tests/test_data/priv_verif_rfc9578.rust.json b/tests/test_data/priv_verif_rfc9578.rust.json new file mode 100644 index 0000000..a17b76e --- /dev/null +++ b/tests/test_data/priv_verif_rfc9578.rust.json @@ -0,0 +1,52 @@ +[ + { + "skS": "6737923c15dd79efe10890c18b7b4a5f17b6b0d1d3296cfba0d6a0e698ec33fc4b67635d2db14255b6a16e8d27541efb", + "pkS": "0229d60a69a11d73842651329db5287cb295d241b19dee60ab3797b9646dab008cbc4a3b2a1dc6f74f7595d85ffc9f77ef", + "token_challenge": "0001000b497373756572204e616d65202b7c8810d81a9e9d4d71272309520f6c510788cc1f92ab00b5b08e6674f8f6be0005612c622c63", + "nonce": "cd51c1db7085669652cc9e3969a7a055044ada9d6b555221c1aa9297c59b410f", + "blind": "9970e21c1630419f737cbe27fca7e743634b85973ce9bb3447c895589d2fafb5f91f5acc9cc8e22116b24b6abd39f451", + "token_request": "000146022c237932f8027042bc01e207458844b8e76ae96ce8dc9fdcad533b19d80216f4c1b79427451d11b949ca2e0e693a7f49", + "token_response": "03456f497ebf278cd7e9333bafa8d7b912cd9013624099ed7453c2243978a8b0f17c7aca1cd84de0d8bae74f7f94dbb6fbd30af9f477f60fc940f75d7b6fd311f92c24c8114406e56001dfce4029f1c9ed7174b312d45e86a241fb2806bc910f549bd649cf0fbc5c988edb50eaf0e582dc0b453b624feafb13fab13242ea390df7bbcec2377ec5ce34326c8913d9bbc3ae", + "token": "0001cd51c1db7085669652cc9e3969a7a055044ada9d6b555221c1aa9297c59b410fd5a776e13841fc0dffa7220965fc02ffc4f2f37a22c32e2fa82e6ebdb789fb9524a5793d5cb7e5214e8d9813b77d329cd02078eeebfa7c75913d6284a50a0a46103d5431d5da65d322df7dac1a938fa7336aaf48a754baf440ffc3bf59152d4da1408721892f1d38cb2b44429084abdf" + }, + { + "skS": "12e53e98e785378ecc44cb4fb890b81a541a70cc477122462543869ea2617b8e7226976f574e16dce895264f2fd058f4", + "pkS": "03f397741d6ab362e6cfd55bfd9bab3503d8c5416b729cde17751af9331d955c7d3ff2669d54947bcfdf20847333f97b28", + "token_challenge": "0001000b497373756572204e616d65000005612c622c63", + "nonce": "b9490832b19141499b2cb0c91530372cd9552a39e013a986be8e287d7e69e5c3", + "blind": "bb5a178ff651d44d008371f0112868b0e0892e13f62dff540b67641ff222575932fad16f4d9f31474baff6050b287b53", + "token_request": "00012502ece71a01f5845f36f3d74ee4c0ca5c899a0e4036ddc34cded12bd622a3bad863f653514a127fadec51c0f2392497da98", + "token_response": "024e8bd4eee645585a87e24bbb9df56543570668816555ad1c79253bf9ebc0ec1b5a3e1d6d42454acf509f65c3f1191830e64bb4947bc8042bc0b1faefd9ab771cf56a3e5e4211d5e820e8d4004d1db766a92d8608bec4d727e8219832fa83658fa3fd40d9d31ae55e8877efa4ef01e9a273ebbb609916cd6f1ad8929b973004f496a74ccdedaec1241237b6ee15ba0190", + "token": "0001b9490832b19141499b2cb0c91530372cd9552a39e013a986be8e287d7e69e5c34d1a8f1a708ab7ec6f9ddfe97074859fdf87737790491e4b3afd0977cd3bd3969331f9f1caf34c932ff14ee04497b8d9ad9c23081b1ba95e9fc597fee311e525173f2bbe381f71f77005e58990ccba67b8768090f3e980aa9a8dfbd78b6c06e52fb9d50b9e2866661afd1e7fccd59f93" + }, + { + "skS": "fff7579de199675d65611093169ccbc1ac8c48e19460bca55dd29cc459819b07180b893719bf3a5f31705a79b1e6439e", + "pkS": "02b4d81e9717b8768ae6940c6bccd1715bfc2e410c676ef2c2a5b8240386ab830276faeb30ee9a1a2da705b31f6b4f9b20", + "token_challenge": "0001000b497373756572204e616d65000005612c622c63", + "nonce": "0a97a9e2b7e8d79155a501134b30b7ea1d7a1b97759e1b9944c255db37a5b734", + "blind": "f1c5adf23be75ee6e423c63b326d796ac942bd88c52f8f8c4cb5e514b8e5c04271c735370d62b8129f7caf9c240e815f", + "token_request": "000189034da0b4405bcf32550fc12234c32a09924b8d457bb146286b60421dd89115d445b98d0c2e4cfb013449253f4e1e6e8df0", + "token_response": "02cea4c15e35fc58ca4b1d52f0b3aad25430753caa95011248930de9bf104c2c50a5bd5a50e647821329fa4dcae26c4778f5e7c653dfd772e1591dc8f71d8b4ee73e175bbc7659642340f9e8e33782c156425ac8763a31585cbae928a947285dd208a1d0b8b5111b2c80b76575e5087a3182082b7e53f40d8bc77d8c379dfde61c9101d488031c09757e72dcc52b8590fe", + "token": "00010a97a9e2b7e8d79155a501134b30b7ea1d7a1b97759e1b9944c255db37a5b7344d1a8f1a708ab7ec6f9ddfe97074859fdf87737790491e4b3afd0977cd3bd39664b52ff09aa3b63897b31c1de93b02073f3bea67e306816a501a77319302f1899eb0e922a3c40a9948a55df1724033f1d99ea61ea84639e7f4434f175483185feb6ec963e3465c5005fe14d1079ba16e" + }, + { + "skS": "64ebc146a8d673f889b879b6008d4349ba77b249b90cf52a550f2d417ecaaa2628f1b62a8ba76f051b27a5e17fbbd1d6", + "pkS": "03cf3e4682b09963068fb687a8dfcf369e0c2c9d370f7eb6bd32a52c2e57f4f8c92e37017b65c74a25fbc8dd2500ee17c9", + "token_challenge": "0001000b497373756572204e616d65207320e79efc103b2ddb1567e1a1d6782c12fc06b659cd93a1e75142ed1138a9140005612c622c63", + "nonce": "584b0fdad4237c4165d5fd8b81f23983c12317397de2901990e5bdc358faf417", + "blind": "96cbeb279b9bd591d91884a568e91730ff12a3f86c04d0e5347d072f141ef4291efff47d65d40c9c0d332cab7ec75e89", + "token_request": "0001b70297e1a5027bd3c1e95d07dac64c9b88fcfbf19dcde93cdc90c5828908ab33a7461a1c24132dc326070c50520f0ee8f244", + "token_response": "03899c0591a858e2f120278efb73b7fa8222868ee646e651ccd4977bf67e764633a7fe8462245975921d7f1e8bdbc352fa947538a8fc9c614a3170263523636088ed2af1157b5ea79377587fe9add36bf7c0a4be5169674ab6081bf834e2da62a2013644913feccc46e10e37b27c4206bbddbad7af47685085583c9358f49fd0954eca8c3be5533aae0aca80a8e9da5973", + "token": "0001584b0fdad4237c4165d5fd8b81f23983c12317397de2901990e5bdc358faf417e3b875dc2252ffb5317639861f1a6ac7be8979a8141b26f1e311cfd129c537d10b8df656c980f3df211226d489766ac0a1bb244557f89b8569d3677b9fac9eb76b0755e0bf27b3cf2171fd61433120cc9afa9b989753036ba1d6924101a2da71c922860fd809511b387aee5fac07d897" + }, + { + "skS": "5c00623e759c48b701c19121df913399bbf27bb9d72dec38a4f4e742d8e0e716599e39ef53c52df3d7df5c5dd36c3ef0", + "pkS": "0342b3ffa300024eca76ded0baa54cbd6406a02103c82ca683755716cdff0b7a935abc84cf056b9052ea9b0eac5506d72f", + "token_challenge": "0001000b497373756572204e616d65000005612c622c63", + "nonce": "586a7b6bb6d75433fad3975af3bc759f0c21d6f1bd3a9ca951faf1f10e4b85de", + "blind": "4236cd88321e67495911060c7d05bd315f9f8a4a0a4444c0befdf8c22b07193d8897bf380a349dcbeba325b863b17980", + "token_request": "0001b503f36b8e9a239e61083577dd5cd0da81c5e503fe010d2a0673e9350eb919f520151816e912c2c9c113371411f951fc2ee3", + "token_response": "034c03a5e0fe764cb57709c152cd4f16aa27420371794c2e164471763e0eda134cfa31f680756bed19004adc40418e69d6f30c99262e292168f08ddd79bb0823c92184960dccf5f6067755e33ba9fc9246684eae1b7f1ed2fba05bd63759e9f8e86734ac06c7a1b2b3f7d62e815e3e017f127783fe62a9f2421991fb25cf5756984cf6ab92a83ad2efdd771f66af78b545", + "token": "0001586a7b6bb6d75433fad3975af3bc759f0c21d6f1bd3a9ca951faf1f10e4b85de4d1a8f1a708ab7ec6f9ddfe97074859fdf87737790491e4b3afd0977cd3bd3964d4e0b4518421d6909aa42a6ad28f368e51702a37fac809251abe2d6e0b824b5a660cac8bfff4ad0aa291681a549ea1d27560d3a22cfd06184d128e98976d9eaf3aae84da62503b29aa2a82855a4bf76" + } +] \ No newline at end of file diff --git a/tests/test_data/pub_verif_rfc9578.go.json b/tests/test_data/pub_verif_rfc9578.go.json new file mode 100644 index 0000000..778c2b5 --- /dev/null +++ b/tests/test_data/pub_verif_rfc9578.go.json @@ -0,0 +1,57 @@ +[ + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65208e7acc900e393381e8810b7c9e4a68b5163f1f880ab6688a6ffe780923609e88000e6f726967696e2e6578616d706c65", + "nonce": "aa72019d1f951df197021ce63876fe8b0a02dc1c31a12b0a2dd1508d07827f05", + "blind": "425421de54c7381864ce36473abfb988c454fe6c27de863de702a6a2adca153fa2de47bd8fcd62734caa8ce1f920b77d980ab58c32d16dde54873f28ca968e8c125b8363514be68972f553655bcc7f80a284cc327e47e804a47333c5b3cdf773312cc7ad9fda748aed0baa7e19c5a2d1dafda718f086d7fc0a4bc02d488e0f20812daee335af7177b7a8369bd617066aed7a58f659f295c36b418827f679725b81ca14ea16fb82df21ad76da1ac38dcf24bf6252f8510e2308608ac9197f6cb54fdcb19db17837302a2b87d659c5605f35f3709a130f0c3d50e172f0cae36cbc9467f9914895a215a9e32443bcafff795273ccf8965a7eaa8c0b2184763e3e5c", + "salt": "3d980852fa570c064204feb8d107098db976ef8c2137e8641d234bbd88a986fdb306a7af220cfadede08f51e1ef61766", + "token_request": "0002086a95be84b63cfed0993bb579194a72a95057e1548ac463a9a5b33b011f2b2011d59487f01862f1d8e4d5ea42e73a660fbc3d010b944a54da3a4e0942f8894c0884589b438cb902e9a34278970f33c16f351f7dae58d273c3ab66ef368da36f785e89e24d1d983d5c34311cd21f290f9e89e8646ab0d0a48988fcd46230de5e7603cd12cc95c7ec5002e5e26737aa7eb69c626476e6c8d46510ee404a3d7daf3a23b7c66735d363ca13676925c6ed0117f60d165ce1f8ba616d041b6384baf6da3e2f757cb18e879a4f8595c2dc895ddf1f4279c75768d108b5c47f95f94e81e2d8b9c8b74476924ab3b7c45243fc99ac5466e8a3680ad37fa15c96010b274094", + "token_response": "675d84b751d9e593330ec4b6d7ab69c9a61517e98971f4b736150508174b4335761464f237be2d72bbae4b94dffc6143413f6351f1aa4efde6c32d4d6d9392a008290d56d1222f9b77a1336213e01934f7d972f3bf9ea5a5786c321352f103b3667e605379a55f0fb925fbb09b8a9f85e7dd4b388a3b49d06fd70ba28f6a780e3bc8f6421554fd6c38b63ef19f84ccfcf14709dd0b4d72213c1f060893854eba0ea1a147e275da320db5e9849882d5f9179efa8a2d8d3b803f9d1445ef5c1f660be08883ce9b29a0a992fc035d2938cbb61c440044438dbb8b3ce7158a8f9827d230482f622d291406ab236b32b122627ae0fd36bd0d6b7607b8044ace404d44", + "token": "0002aa72019d1f951df197021ce63876fe8b0a02dc1c31a12b0a2dd1508d07827f055969f643b4cfda5196d4aa86aeb5368834f4f06de46950ed435b3b81bd036d44ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708bc6a21b533d07294b5e900faf5537dd3eb33cee4e08c9670d1e5358fd184b0e00c637174f5206b14c7bb0e724ebf6b56271e5aa2ed94c051c4a433d302b23bc52460810d489fb050f9de5c868c6c1b06e3849fd087629f704cc724bc0d0984d5c339686fcdd75f9a9cdd25f37f855f6f4c584d84f716864f546b696d620c5bd41a811498de84ff9740ba3003ba2422d26b91eb745c084758974642a42078201543246ddb58030ea8e722376aa82484dca9610a8fb7e018e396165462e17a03e40ea7e128c090a911ecc708066cb201833010c1ebd4e910fc8e27a1be467f78671836a508257123a45e4e0ae2180a434bd1037713466347a8ebe46439d3da1970" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c6500000e6f726967696e2e6578616d706c65", + "nonce": "98c1345ff38a554b429b428b0f206cfe4f3892f8041995f2c24873d90e84488d", + "blind": "7bb85f89c9b83a0e2b02938b3396f06f8f3df0018a91f1a2cc5416aaa552994d063f634d50bea13bffe8d5e01431e646e2e384549cefd695ac3affff665a1ebf0113df2520006bd66e468d37a58266daa8a3a75692535e1fc46d0c1d6fb6f37c949808172e20c0b77a48570a1fcb474325bdd23cdbce52b5d6a9e39f7aec73b09004eae8c8bfff2b4b533ea63bcf467a4cd95ccfb0cb4e43bc4992c1fd0be7a77a4475dbf8094cf25125ece901abbcea607a9050ad9f8ec3d0d66341f6eab40ee9c9c22c0b560b8377f8543d8878c7458885fd285c7556cc88fc6021617075b42c83a86005169a6f13352e789b28fdbbe3d0288e1dd7c801497573893146aea3", + "salt": "b6b4378421ab0ea677ce3f4036fd0489dee458ad81ea519c3e8bde3fcd5ec1505d28e110d7b44dcac5e04ecedd54d11a", + "token_request": "00020892d26a271c0104657ba10c0b5cb2827bb209d86e80027f96bfb861e0f40cb897f0fc426498433141ce9bc8b4a95914fefe4e40bdd3802a121cb0b59a4ae7e03255275c4abf071d991c82ead402606c0ef912178b0a0f68d303e06a966079230592827b84979dbcb5f21ab8904e9908638ddf705c4f8af8a053c19a66090726b60c6b4063976e4c66eab33522dd3f9d64828441db4aa82d55adcc3d3920592884cd1e5a3f490d5c81f1306705dcc5c61d82373f1dbd7d2ae4b2fea0f7339f5d868415f59312766e3074ee4a7305f5f053da82673ee6747a727a26d8d10ea1b1a3491d26b0c38b962c02a774ac78932153aae9dcc98a9b1db1f5389644682f7727", + "token_response": "113a5124c1aef6fc230d9fc42b789226f45ca941aad4da3f48cf37c7744a8d7fd1dcfd71cd39d09e9324760180ea0bade3360efaf7322a1fa15f41247be3857fde8c5c92ec6d67a7ee33be8fdadf8b27bb0db706117448e55bce9927cb6bfb1f87f9edb054181a4558af0c0d3973d7033b9599e674c20cf08a7bbcf0da815a2edaab7c4fb80dee4ea2cc53576a9691e857da931c6c592d2c69dd21afda8ea653dd90157adfe80e2375c08e75beb497df8b7b73192fbbd4e80359d9bbaecea14e0acebdda92596f71ec1d57e26b6497b3152976bc07a4409148cb84389eb207fb8e841106012408c6e19b4f964008b6a909aaab767a661a061c97da1643040455", + "token": "000298c1345ff38a554b429b428b0f206cfe4f3892f8041995f2c24873d90e84488d11e15c91a7c2ad02abd66645802373db1d823bea80f08d452541fb2b62b5898bca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd27083350a206c5e9b7c0898f97611ce0bb8d74d310bb194ab67e094e32ff6da90886924b1b9e7b569402c1101d896d2fc3a7371ef77f02310db1dc9f81c8535828c2d0e9d9051720d182cd54e1c2c3bf417da2fc7aa72bb70ccc834ef274a2e809c9821b3d395d6535423f7428b3f29175d6eb840b4b7685336e57e2b6afeaabc0c17ea4f557e8a9cc2f624e245c6ccd7cbdd6c32c97c5c6974e802f688e2d25f0aba4215f609f692244517d5d3407e0172273982c001c158f5fcbe1b5d2447c26a87e89f5a9e72b498b0c59ce749823d2cf253d3cf6cd4e64fa0e434d95e488789247a9ceed756ff4ff33a8d2402c0db381236d331092838b608a42002552092897" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65000017666f6f2e6578616d706c652c6261722e6578616d706c65", + "nonce": "9e7a22bdc5d715682434cebc07eb5fa53f622f776a17a6d91757af1592df0e71", + "blind": "c52cabc5e4e131e0f5860cc4c486c5ee8a5fa8ae59484446121f87b0d8ccd037f161a99ebcc57f79d05a2ffc852656ad2d0894fab8d1b0f998e6e678254ed5778da98b137371320314d06c24276e35435bccffa49d257687f270f9ce17926a074737546d5415a4bb9e624a6302562b395856632efb6992f6593a4f95fb342002efebc3046ca96bbc26edb2f1a1454a24ce7b9a7ec8e44fb9e99c8144d409d8cd8a5903c0a3c0acbd9f82573ed1fc4a296e3eaf4867ade30110794678f422d36bd103ea4617d2472cf58da3381e52e5be60f4acbf685e280648cef21211a796ecd005ecbdaa1046c40950afca4c4e7dd4b8c19e504088489a15667b45895b6e92", + "salt": "c847b5d0fa9101a1e09954ac9f3eed6600af58936295ad2e54274e13e640d59f732d07530c94c19c20668f03470c77ac", + "token_request": "0002080f6bd84fba1822c577c8cd670f1136cca107f84ddd9d405d5ed22ad15da975538f031433bad4a2688999732927efe2928d4c132389a122f40b639b083d6fcbbed7a55fb18db536d2dcbaefe6dc0a70730e6565b08a7dfd783913a59f37d798de0cfc262c9e90a7ee884a3ec355eacbd44e5f6779fea6a785b05ac352fdd51a116cf2be1d8e38b0bfacd6a3d53a88c99f747cce908f86b33562691f540e3e88562092cd17cc2f78ce0fb53312a5f2dc918bdb1dc90d9d65091c7ba9080ccc1755cb5437989364dc92f0e8fea18f66d631451feb02a3d68af41de1a3f9be925dda5c4ca0706fc4ca28b3317e939f6573442c6d03be17cd141fa8260d382d134c6b", + "token_response": "2dd08ce89cf4f62bc236ab7b75266e13c57c750345e328e0bea107537c4cbeea5bfc990716950440628ea2e37dbc5c9c6d84f9a965cbf0cbfffb89516b1fd19a90d69cc52a28890bbdcf782f56aefadad85b6e861a74170ce910891c89e4293f37978dbd41cc8b5c68802de3d86d9f0326b9c22b8095122458966a6ddd1aeb3828d239c3b359efc9b375390eb19050d5656c2b084304d9bd8a81614f631bf82a7e4588413b44a0cb6d94e942fa134790b396cb71e3ed33b557b5bd0734e726fa79abdca8694703b81d0e289b749801d4383e0d4f825dcde0dd98c43d3ba81c028dd8833a4fc24961f60e118d4421dce5b611d53e9ca96156a52509bfa9afeb7e", + "token": "00029e7a22bdc5d715682434cebc07eb5fa53f622f776a17a6d91757af1592df0e710042eee45ac4dd5acb8f6e65c4d8dd47504f73f7463507ef96a4d7227d2774f3ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd270815b010bbc0d5f55e9c856d2e9ffaefba007d33c2d5452fbeb0b15919b973e0dc9180aaeb18242043758d9fb0ac9ac5e04da9ff74ec93644ae6cdb7068ea76ce2295b9b95e383ed3a9856e9f618dafdf4cec5d2b53ea4297c2f3990babca71e3ccd6c07a437daae7ed27b6b81178fb7ce5fa5dd63781cc64ac1e410f441c034b0a5cc873a2ce875e8b38c92bab563635c4f8f4fa35d1f582ef19edf7da75aa11a503a82e32a12bd4da41e0ca7ec7f451caf586f5b910003fcbbb9ff5ffa2408c28d6807737d03da651ea9bfafcc2747a6830e19a1d160fcd5c25d2f79dad86a8b3de8e926e08ca1addced72977f7b56398ef59c26e725df0a976a08f2a936ca42" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65000000", + "nonce": "494dae41fc7e300c2d09990afcd5d5e1fc95305337dc12f78942c45340bfe8e6", + "blind": "097cb17bcedecfe058dff5c4e517d1e36d7ab8f46252b1ac1933ba378c32625c0dbc69f5655c2003bf39e75810796cd63675b223cf3162c57108d56e0584cfce6cad829e74369ada38a095eb3012c912b31ccde7425f93464e353fb17552be3a8df2913daca61543a33ae45058f218c471dfbc12fb304158e29b6ed35bc079e23f1e6173c5dec4545840bbe58e5ad37cbea0a10dca5d9df2781589d27c34108477b52c0d32a1370c17f703941fbb1a007a6794e7de2758709c9bbf80f21eec7922b9bb491eb6aac8c1a14764e648e6be4fff0ae913797067aa0826f366c3103e103b05653c73b52d7f825a185dccfb806da700db9f53abb848554b7d4f7c28f3", + "salt": "49912979f1bf528e5b8228ab1328df74319dce7bdaf45821ceb1100dcf042a2dfe852fc9db59b64a5f6493c282504240", + "token_request": "000208244840027ca8c620f8b14caded9a198ba388ccd8541e962f68a0071535d958d18494afd0bc11da4da8c8b33864f5a8f623b697cd56348594e11a75479048a72c0ed179b070506c09a7eb6ed3582f572df38cf60fcde11a52c5ce6d7b23435b60200ad9f66d21f40f323c9aa54307d0b966d4457c37542b66bb183ddeafca914fc74831698b5d52f498ee3d165685f49a8d86e39fe6c4b7ec678f5250908d25e5b873c69b422368121aa4210cadd6fc640907d3cb9a7a3e827a0e742470f00c2f49dc6c0e8cc9470dbfd73df0ccbb96c10b02af0dd7dee719eca11ff8e1b4929e59f3cf319de9bda29a6d968b43083b5d4242f3448d76ada08b8014f70b97e719", + "token_response": "c2746ff644cffb28a2c19395fa19dfb61fd135daa837844fbf9fbe06c253e64e69f53aefddc0fb4833b1b5e58f571134a34f245499c3e73419549c2c9111cf94f2f68fea3996d47f71e8d8d6fc5b1c074bf74fa59de4cbf32f5f08d45ea45492f0279c3b1a8d852698edbe1651eb8e09eb223a27386c0feb2f6a8260235edb36cf433da518100829b63166284b325d87fc941ea3bafe7b6761b7082e09397837f74b4f0fc838bce8af7242089dd5561f57735926bcbad219fc9fee85ae49a8e8951f63ca194b7ff018c06ee02267e7267bb996432dc76973819da80e3e86947b0a4b36d3a972dafaaa3db0e1044b325f02c679996d9bcd3ce51390d54bc10b8c", + "token": "0002494dae41fc7e300c2d09990afcd5d5e1fc95305337dc12f78942c45340bfe8e6b741ec1b6fd05f1e95f8982906aec1612896d9ca97d53eef94ad3c9fe023f7a4ca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd2708a55c83dc04292b5d92add1a87b37e54f22f61c58840586f390c50b231824423378ddcf50e69dc817d45bfad06c7f2a0ac35d2acd7f26b0bc9954c192b0a0ef28a2a5650e390098dd3cb1166a7cb1716d3dd2d19dc5ca3b1ea6206359de0002d82bc4fa7e69fb07214b06addcbd2203d1e17f57fc580bcc5a13e0ac15cf942182cc2b5d6eaa737a712704114e357e2ec2f10047463ded02a1a0766dc346dd7212b9711e03ac95eb258ac1164104dc9a0d3e738ae742ab5ed8c5139fc07145a788b9f891741ee68f0a66782b7b84a9bb4cb4b3d1b26b67106f397b35b641d882d7b0185168946de898ef72349a44a47dbdd6d46e9ba9ba543d5701b65c63d645c2" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f49424151444c4775317261705831736334420a4f6b7a38717957355379356b6f6a41303543554b66717444774e38366a424b5a4f76457245526b49314c527876734d6453327961326333616b4745714c756b440a556a35743561496b3172417643655844644e44503442325055707851436e6969396e6b492b6d67725769744444494871386139793137586e6c5079596f784f530a646f6558563835464f314a752b62397336356d586d34516a7551394559614971383371724450567a50335758712b524e4d636379323269686763624c766d42390a6a41355334475666325a6c74785954736f4c364872377a58696a4e39463748627165676f753967654b524d584645352f2b4a3956595a634a734a624c756570480a544f72535a4d4948502b5358514d4166414f454a4547426d6d4430683566672f43473475676a79486e4e51383733414e4b6a55716d3676574574413872514c620a4530742b496c706641674d4241414543676745414c7a4362647a69316a506435384d6b562b434c6679665351322b7266486e7266724665502f566344787275690a3270316153584a596962653645532b4d622f4d4655646c485067414c773178513457657266366336444373686c6c784c57535638477342737663386f364750320a6359366f777042447763626168474b556b5030456b62395330584c4a57634753473561556e484a585237696e7834635a6c666f4c6e7245516536685578734d710a6230644878644844424d644766565777674b6f6a4f6a70532f39386d4555793756422f3661326c7265676c766a632f326e4b434b7459373744376454716c47460a787a414261577538364d435a342f5131334c762b426566627174493973715a5a776a7264556851483856437872793251564d515751696e57684174364d7154340a53425354726f6c5a7a7772716a65384d504a393175614e4d6458474c63484c49323673587a76374b53514b42675144766377735055557641395a325a583958350a6d49784d54424e6445467a56625550754b4b413179576e31554d444e63556a71682b7a652f376b337946786b68305146333162713630654c393047495369414f0a354b4f574d39454b6f2b7841513262614b314d664f5931472b386a7a42585570427339346b353353383879586d4b366e796467763730424a385a6835666b55710a5732306f5362686b686a5264537a48326b52476972672b5553774b426751445a4a4d6e7279324578612f3345713750626f737841504d69596e6b354a415053470a79327a305a375455622b7548514f2f2b78504d376e433075794c494d44396c61544d48776e3673372f4c62476f455031575267706f59482f4231346b2f526e360a667577524e3632496f397463392b41434c745542377674476179332b675277597453433262356564386c4969656774546b6561306830754453527841745673330a6e356b796132513976514b4267464a75467a4f5a742b7467596e576e51554567573850304f494a45484d45345554644f637743784b7248527239334a6a7546320a453377644b6f546969375072774f59496f614a5468706a50634a62626462664b792b6e735170315947763977644a724d6156774a6376497077563676315570660a56744c61646d316c6b6c7670717336474e4d386a6e4d30587833616a6d6d6e66655739794758453570684d727a4c4a6c394630396349324c416f4742414e58760a75675658727032627354316f6b6436755361427367704a6a5065774e526433635a4b397a306153503144544131504e6b7065517748672f2b36665361564f487a0a79417844733968355272627852614e6673542b7241554837783153594456565159564d68555262546f5a6536472f6a716e544333664e6648563178745a666f740a306c6f4d4867776570362b53494d436f6565325a6374755a5633326c63496166397262484f633764416f47416551386b3853494c4e4736444f413331544535500a6d3031414a49597737416c5233756f2f524e61432b78596450553354736b75414c78786944522f57734c455142436a6b46576d6d4a41576e51554474626e594e0a536377523847324a36466e72454374627479733733574156476f6f465a6e636d504c50386c784c79626c534244454c79615a762f624173506c4d4f39624435630a4a2b4e534261612b6f694c6c31776d4361354d43666c633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b7e225a5f0203010001", + "token_challenge": "0002000e6973737565722e6578616d706c65208e7acc900e393381e8810b7c9e4a68b5163f1f880ab6688a6ffe780923609e880000", + "nonce": "a1aa8b371c37c9a8ddbd7342ab4f9dd5227d5b1600dca6517b60f63143cd43a3", + "blind": "ad7a32e1ac31b91daefd7042cc23d5621ab3e870d87297bbfe1ee8a518ffc5b84770d3b77775c485b2d219954834868842d2f11877ac4bceb5da88944cca043a9afa52f9c9998a5dea7ab7c1f82662d0d327e29705a269ad221ae74a7c1172ff89c48997a9fda08886d3998bb538868396c0ace71d260cc71f768001939b24d80d88979f0244a3dbc004eadfac81e138d430b9fa51c1aad21b957ff96b3123c91c2fff362a386f0f99a3f9fc906ca626fd9107648f87532b44c4fe3856ecae1f46d8ebf5d2f46e52034478e5e883015666574dd80bd5c036c4b55ebcc8b660688d23944cc1932d075b559dcdc269fae3511761f71c113634e60d67accc8875fb", + "salt": "35c04710ce866d879447b6230ce098a49e81be5c067881cce7bd5f92c1e5bd9b3c7d4d795cfad134fdfe916d735a624a", + "token_request": "0002083d6495c72529bbc4f5c0b49e94e4561baec1ca638a93b2940ea9e37b838db7b1a91ec1f257d49b45c4f75119c2ab9eb5578541ad2b9bac1bd627abc709097f503f83d98fed6dbeb615c3be9bf09cbf8ea25ea8026c1b8ba1c704ff516ed87c3d7d85342fd00111d8a80492d4b8fdbb092a282f74f13901e5edc1b3b02cfe24c950affe6130fbb57c1482d674db3c6944812ba081c2235a16d01eeec0932a8619d85732fc3e36179f0b50377bf9cb7a50ce3abeb3f31ed5f0f3deec7aae7290f5397cec61318357d652b029a0fda0f100a78e36c4ef56ba3779963e8745fdf4e347763c63d825836878e249833a0f4bd315392cc06ccca2c955e921efbc4f941d", + "token_response": "8db727000018a98a2fe9fda8bbde5b8e9cedc31efbcaed6950eb1e0f8d9af9db632def52f74f07cdab304bbde40519080dd0388fb2b8900528b4791d2bca40aa2c2a6d1b92f010c1849bfb781cc813cc204855dd05e8a2dd31ea5220981b8ab6b008e153083dc8f594206440d66286fea9c21b56807be8655506ab7818bb9c8c69489dda56fe6390a5397268c8b5711f9d2df6f2584740cccf0345fd67f93f345426f33c078a0aceb90845df9eef74f6248d06c36d19e191da325b721ddc12ea78ed37b0c3b6170590536e3aee7eb0efc7d11a2c9d072a394f12ffa67ecf316c49efd8f31723b11fe46740636bd89ad4f7ef96bc38b2cb4916d9dc04ba1b2fc6", + "token": "0002a1aa8b371c37c9a8ddbd7342ab4f9dd5227d5b1600dca6517b60f63143cd43a3bb8a8cf1c59e7a251358ed76fe0ccff61044bc79dd261f16020324d22f2d434cca572f8982a9ca248a3056186322d93ca147266121ddeb5632c07f1f71cd27082899f4bc385b4147a650a3e7efc7d23a743cb944bb53e2ed8b88ee03a9c0b1cf1f73fd7f15c1bd1f850a5a96a34d4ee9f295543f30ac920825e9a0ce26e924f2c145583019dd14408c565b660e8b702fbea559908b1a8c8f9d21ef22f7cc6a4641c57c146e6c5497d2890ca230a6749f5f83a8fdd79eba0722f10dff9e81a2fb2d05fa4d989acc2e93f595ae69c98c3daa3b169efcdd6e59596b2f049f16ba49528761f661032da65a3ee0fe8a22409766e90daf8c60323c16522818c49273c795f26bbab306dc63cfc16fe1702af2464028cf819cc647d6f9b8a8f54d7d6585a268fdb8c75f76618c64aba266836a3b2db7cdd739815a021d7ff2b36ef91f23" + } +] \ No newline at end of file diff --git a/tests/test_data/pub_verif_rfc9578.rust.json b/tests/test_data/pub_verif_rfc9578.rust.json new file mode 100644 index 0000000..e0b0299 --- /dev/null +++ b/tests/test_data/pub_verif_rfc9578.rust.json @@ -0,0 +1,57 @@ +[ + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945766749424144414e42676b71686b6947397730424151454641415343424b67776767536b41674541416f49424151446b46613063674c654c477a36740a54416b5a5a6644337430616d30437a79794b6d5271344b305a2b47547232514b3976566b35506c544b41624b6d64314e3873465044596a7679734133326b636a0a2b79763171713530493164444433645379564a31467241375169594355544e65416a576f585937383739336a61484c4e4f59634b675a45594554655a6d3247670a426c495345517572362b6a3774444b78416d754d4f31377341704433585672625977696e6a446c38557253446b74615036517230337a3558324d6d50552b4e630a3258444f5a6b4569556d3945326d544d734c50436b49626765384b387478314e3875797345656376462b5252474474734e34627642357259365776644e7670440a4a64476e7154446b7a65454e3344767858624c484f35716e4a306a334c4857505a52484564555a78486c48596451686e365a497965663648676d45326a64696f0a71486b65666d525841674d424141454367674542414c2f704f544a6458354a4e52307837534d4331464b377036436d647965517955504d2f7334646d544e68390a34426956754e362f42735967544b36574469357a447049636d6c6268617944547162347472637855593974576a5372677a593637314953757058514738594a4c0a6e6f51414b665054584f655955455079383332595a50624446446d43367878314d4178387050754d6d6878374d4272786753474e48723773696137646265716a0a624a5a6b5a65514b484b6231316534504f67312f347658366230585145435377546239396250555a4c4c62563550315a48725674654d636e64473541536b6e310a7a466b744c34374b705271334f4c534e53302f73536a4c73506c6756722f6b4e636a524a71445550673351453853635555593330724d6f696c37735779716a570a694d526c59314d4a583264726f753773302b4253514449316f734c525156557735594c74504b55676c52454367594541386d7036716b54446b666d53697573570a7849475347436452577747357869414e70504670754657704a6c4739364f6f4a575a70746967626d6c676168413578416459754d71724c616f514a59426336740a4c5067456e42694e594f464c47787134525a516c776863317841354679533044494c31644a6a4c7479304c394942324f65784b54734c32714e6e425857315a4b0a4a6d512b2b454d48572f7763426e4272382f6b6a315444454e6b6b4367594541384e326366744e52514659484244585530666a35716b4677474a70666a6157640a2f413144553846656e676a47526a376c6275656c7a507a4959697033752b694e773531465a6b58712f6e305a6c554c7a627a644c7a686d5374616a72526753740a466d7351476c633730654547777930664b755333686a45776f66353170627250435936766f53666336346d5147655538415664504c655875647278736e6469720a4d69567043635948525a384367594245425061595569556944346f37366f7a2f35473975305a4631584c325978794f73395951356b656564646641347133716d0a4a72753373416f42624b692f543565536f2b35376379582f615a337a4f7a5043573346487232663047434d7633735a3331344c6b764168394535676e5137634e0a393752735a4876757663517976376862396a463074544e4f68535574337239396d4546472f642f5277714c4764384d43755253423347596836514b42675144680a35585759484766666873386479373746446c5259735061496e48397851527069416135624a736575503178314652666f59366a454432392f5959545a33736a560a3550487063546c72593768536e472f69706d475a3862482b5857594b544f686f5668444d49335165366f53464742526244685a77374546494c485835384c47720a725035616a69424c5a734358434c3632493052484c35587546646c6f5549522b693064767579476d30514b42674455625442774a5270446a34646a34542f4e520a556668504e2b6865486a6b4f6b384b37586231553479352f705966786f34304c44772f49506e4a69313765767852395a30332f686c4537393176503258664b350a4c2f776f6e31356a5a52507a38722f72555230763034754870385a5a6b496c557778696876323953652b6a74395a7376794b6250346f394f6b4f7630326839740a6d536c4457354d2f626c79782b484f737752484f6a6933330a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100e415ad1c80b78b1b3ead4c091965f0f7b746a6d02cf2c8a991ab82b467e193af640af6f564e4f9532806ca99dd4df2c14f0d88efcac037da4723fb2bf5aaae742357430f7752c9527516b03b42260251335e0235a85d8efcefdde36872cd39870a8191181137999b61a0065212110babebe8fbb432b1026b8c3b5eec0290f75d5adb6308a78c397c52b48392d68fe90af4df3e57d8c98f53e35cd970ce664122526f44da64ccb0b3c29086e07bc2bcb71d4df2ecac11e72f17e451183b6c3786ef079ad8e96bdd36fa4325d1a7a930e4cde10ddc3bf15db2c73b9aa72748f72c758f6511c47546711e51d8750867e9923279fe878261368dd8a8a8791e7e64570203010001", + "token_challenge": "0002000b497373756572204e616d652067c589a97fd0048838a4ebb44d782d3802db23e0f3055e58bcc36f70be3f3fc00005612c622c63", + "nonce": "f435a6a430f5aa88cc4701d0e282897f405b789dbf5b2c618f6ef891409cd979", + "blind": "1c6da669ebca3e216eea5e84aa20cffe998ef41642df4e36b68aad606279c7ec577b0ccd306bc5510ad77ec3a74e1b613e8e0e3f92276bbc6e99358b2149fb7c45d046be8cc7b01ce9d9610b65096f8bab77349118c21d5c757e7abe12cb8a10cdef8379f09118f24b447d18dd22ff348d9640adfe8efe0ff070236159b3e4d1003a47038c7c5d7a812d01cbb45ef19f985d80afd08357ef4be441e887450db794dcb9da6b0e42403bfc6f73ffa362f2a945dcc180e10020797576a37568422e3646a9c3a2f704cbaf1f538b44bfc0965698ff3c41b1a2d4b70e5b0b147977482243d55df84df9aed52cbf6275db9d33bb43850f688f010005a94a770b11dea9", + "salt": "aca64cd777148506da3157bf38414402983b64ab7a2320f0622a8cb511461b5e44dc5209b14623c7519d075d5e44cb6b", + "token_request": "0002d50e378591ecbfac7f96052a30a33c54d2090446205c123a94f8bf039bdd3944e1a031b9aeb76ee290eff1814e2b9db50233f0df2e61e0250068bf315380afb8dbd718d713d61431d4d9c9b4f6ac94d668925253db18c9bd09b6039b28365374919e02d898318695322990f5ac8d1fdd180ecb179118d72ab32bee105d291b7b700b3ab67f1567fdc9f6a22ab0e1754531717cf2742790b54173e2dacb0c1496f5c49f7cc6cd23077787d39fc30828cebd0894ba91dea9266b6e477ac7befaa0d8110c33dc7f07a3d704ca88c0a76d166bbcff5533545ad7fd208fb70e8e295f755f48d83a8650ddbe67f9e6ecbc252b00c41ec0b74c54b11f8fdaeb02c4a73b7b", + "token_response": "44f2f185b641baa5f3cadf788e1cb6ae59cd30301ca5899c40614bc551dd9362d8505d4cd952db7a608078cf826156bc153baa1ed571d9d90143cc47aee88487a62b80f46397f26c21a19739478475f56ec4edb75c514e785814d1261ffb41f0eb9edc5a8f87fb4f7a29be9f9801680261d15f794da1ce2d1603201dc91d0d2642dabf2e9d775280452b05aec4cf4c2cad70a121785f3d339c3ae5f6476d0776dddf93b7d61401f3002482e564fd9b2d0a0c661efe5d10eb14e5cb45d86a3dfaf2008a7f8c84b8645120d601e53044ce2564b2ce6c2bd2300b540715e7953553354d8db8507414e57ee5a7644329df696dbe46c587c3729237bc6507e74a9eea", + "token": "0002f435a6a430f5aa88cc4701d0e282897f405b789dbf5b2c618f6ef891409cd979365bd3b15a3d93423ad354a1011c0ffb38d20a8fc10a29ef38a91124ff5c10a57090dee961a3ee68bd88c7843f1fd1801c22c32b4a34fe369e1f5c19615476d51c3d059c244e816096779971647f4b2c8bf4c877f88cdda4194f6dd5153bf95ddbb57f22450b7d1237cf66d577a6148ca4f8ec3264d6e92a4d5401e2b2b75a72c5d129e2d65f924b0738849cb17b9d57967deaf7421ecb0080a473cfadd09e67fa20397ee8d18ef27b2fcfe28d14480ba3e74150ccc478e0a5d999f5e89c9b66ef21a5ede765783d3fa6ce59391fd2831af111f2945248a92f1220910cef3231813c37c66376fb3fa59c4aee4064ce3f335a606593dc458ceba0540485daba068d241b7a46595d4ca814654e42051580f16a0f0d56ef0adee6edf80d3f27d186030222abfd3f5f1a7686158b4ea300fb0cfd43996a6693c6b4b6e6d0128f1bd3" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945766749424144414e42676b71686b6947397730424151454641415343424b67776767536b41674541416f4942415144646441385075745956467164700a482f4f5a306569746a6f6c62664530364c78774f636a7a3646463136667531696b6578397339534d502f716249332f537158774a4f70595175617a46586a56430a4e326f36526d574b35566d69356732594270516136527432574d694b736f747945487a384650653563684c4c76586130534e574656674c5a31614150544569740a45516d6938693352386148684170774a6e735076766c6a6d6d5656315943345634395a6f5071366944524c6e647231796b544a39662b747964574265396a66710a4874554e655a31466b784f3642713658634e3837772b733959797667764874323647615653786b794646673758434c36502b3838584c35424271594364326d6a0a3669784e75544b32785871567558506a5171654761506c42474d4d6e55396442464e71766467646561672b48677964623943525a68487634775a64786d6532390a345138383554655841674d424141454367674542414a6f61476955444364695139427857544b6a63686b4c48376c4f495579464c435159516674677a646230350a4f433267682b594e54726e754235455069664a705746502f76632b5474574e2b70436359387358736d4e716e734e474872797638582f472b42777336797771680a4a694a51305069474373433058646f44564f677479732f4851364b63596f35697369776b7341554136397574667331616949533151336e4168704b70587575570a6e2f457a7a4352626136766164373135483858546867426f7a7a72436a584176426732396f6f42634c45535a68346e6b63413645554a665a476e3947704a51380a566531344359694a5a366f6a59373236424465746b797a4831425172714165744f4e7756536d6e6a33654c616a7462495861444d34314a414a6e32744b4364700a724f3050596b31396b562f57434e6c78775a6747674e70433665654250415534385149574f59464c38314543675945413670704d6c776e33536c3947574444760a5572526433576549474879336e782f497076346b5066426b314d716e4c35496d6a6a6841734846306f575042567a7864694b45656d5238706369744b4e3474680a6b36486a567a38326e3449696f48316a674a6431686a58327a3439437238624932746e4e5a663466454d53724a39366e757663773938707a4578556f437471390a6476514c3465334339544745754c4947766875594f334558656a6b4367594541386161387251614c4b42794d555947795042694237725035784c4671655272480a4b693543485869482f7272434a696c6561573545396f6d4c4e6255412b536e6c31522f584f576a765256676e344d6f4a6f47327a45457375574452692f596b690a686c46774b544147536473787146634e69633061386c54302b6f4c2f636255725945544533564448574a6e76384d754b705a7a4e5466504479786f37505544590a75727637506133616745384367594231766c4e4b53746f7548587330722f7174533545416d6747594b4b4150344f665879423177614d4a7965664e47304e64410a38634b6c79347435704a4e597352645168327a50394f2f634f5258616f72327156736d2b394237364631704271697a535554484f316936314a585757757648370a784f78746b416b505a57567863544a46682b4b76756f2f65742f4f4d337850514b395036354658417363576d334e4b396679503855566f7a49514b426743756b0a62566269593664753448696e37385369754853663148414e69716b73694f4f35547639376b5451554470524e7a4674474b765a4c6950532b4a397259456b59580a5a6774635244704633593366627a576d476a7a4c46796565376e48546e6f53626d4b4958706b73595a7a4e3735566f4a7a4f41504c39382b59567672324657650a6c643953673234672f7843423868784f3456552b7652476c5079576b33483939433762722b785a4a416f4742414d50756e4e71324b5839726259506c52466a4f0a4f326f74492f776e43397971545461724e7053496e454d3370796f686f53462b4733726b41774c6e54306e5a2b6b496241365632796f345750524c2b4f4158520a42484a374642642f47783770457a6166716b6a766674797038533233306c4b4469316f586a42586a754f324a714e4253674f6671512b3175354e394f563631670a516236654d69326e6e704742642b726332625171375433510a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100dd740f0fbad61516a7691ff399d1e8ad8e895b7c4d3a2f1c0e723cfa145d7a7eed6291ec7db3d48c3ffa9b237fd2a97c093a9610b9acc55e3542376a3a46658ae559a2e60d9806941ae91b7658c88ab28b72107cfc14f7b97212cbbd76b448d5855602d9d5a00f4c48ad1109a2f22dd1f1a1e1029c099ec3efbe58e6995575602e15e3d6683eaea20d12e776bd7291327d7feb7275605ef637ea1ed50d799d459313ba06ae9770df3bc3eb3d632be0bc7b76e866954b193214583b5c22fa3fef3c5cbe4106a6027769a3ea2c4db932b6c57a95b973e342a78668f94118c32753d74114daaf76075e6a0f8783275bf42459847bf8c1977199edbde10f3ce537970203010001", + "token_challenge": "0002000b497373756572204e616d65000005612c622c63", + "nonce": "8e1e7e8af6537be06c0d284d53f547433ee2de18a850bc687d6945b7eb4d83fa", + "blind": "08e335b44afc640a61195838e3af89662690f7bb18910c84ffdb7f2c863a81a61dadf4bdec0ae8fd792858241ac87f6361d6a5118f8ba13f753ef1603dd0ffc49d5790a4d6ecdd1efcfa3554cf5d31009fb693606a8620d8097a10687a0769c89ee6aec7cef80c93a033dd335624d0fbed3b928887d76cad7b91435d208fb48db430560be5c98fdfd0a69ce605c39fa305c316bb36fe7243ca8628eb7b2c95af844b02310348e512fe69512a9aa6d11aeb20ade9c19a6a10246b555dc7562ae1b73bbb98d5342fe198c8c44f842abad48c2df84d9d5f607d154e8b345be5cd4fae966c7f25114fa27ebdb5c9ea3fe0684a3561846bfff8d4a99b01650392eb93", + "salt": "c84b08734c01f9d145ebe035c9d03cbc6ce0eb5640df0bca3eff07f234543b47350572f9713dea3db28ec0fd25e1e1be", + "token_request": "00026049343b95396fbf80723bbb4cd0fbc14d490a80642d0436afdeff74bbd4694a8bd5d4666ec12e6246f0fee1d19cd39b192c738c84c0ba3df73659207b27c8e95b36a336baff9ffb55e661579f36993a199ca6c802b2a553eda6ae49a795d67afa9185b5b2cc9c41a2db923dcb1711c6864998fdc3a041df885f3c23c7d1ec79a726b14963a25110a68c1c4edaaf55164d5cc18a8f3648db7fced085ffcb8bc5d904f4c8ce9f27dfc0c0ed9120475baab61772e7553b468ffad3a7ed8214a1724776533c46bf535e10aa6e6b0ea6724b71f765dd701ad71d3dd74a4a2e84e710e9ff1affa481701b0ed65278cb4966ba43e116a4413b76521a4eb15a8898fae183", + "token_response": "49264dea6835101bbc1431205acf998b6885dcf1e394e2b33640d19863dd257e43a4921a19a8e783de001c434dd29fcc56337c79d876d72f7e11e5ea9e3bd52521c41c9520a9aa9558b55c2e518b645e1d953ad5a90be443ba8df0404315beae13d5b9417b759832a85a312c4e228fbb3db332c1dbff7371c28f77e9f9055c3035408cfeb16a08c68587c9f5e479b1240e76f3b709701d753b442dba64d671a3150647d7c8966d3ca6ba5ea7db4587ae73a96d16e3e7c4631796af108001eebc89c175f2a19dbb25709d0816432d0c1fb93eca289d71a1c4405a96e19a70b2167fa20e21b0f9a2a5cc34139733380d47f1dfe6f605fc2544ecf7bca93658b9f2", + "token": "00028e1e7e8af6537be06c0d284d53f547433ee2de18a850bc687d6945b7eb4d83fa201c8af95ce37c05aaa2acadc50fd6d8a825e992c5bebf2d617f0359cbb669d8d7d1187a678ae95b2f12e690f0519ec2bc34bffe5c5c4cdc0ee04ce77749d060d7d042623bae4a88a2f7cf2d303a32ef797e0f879e9c1046b6977ccd2042611c958b7b54d178c995c8049df861b18eb56f813a33aeb6cf25845ff00af9a1e5c9f33e2669639653a868e01d7f8d7b1c86d53d7157823b50287ca617b6d937dcc0ca2e1baaf115dfb8309769399d0541cb3f36272b3d3f142fe5b3af7b2e65de50e487fff5ea4d99bb713141a3ad95835f0c1ed77283e457f901a718511bbe9855c41c7903022619a9fdb018ecf4e6635660546612bb1665b396e71d62ba2cd7df42fde34055b98b67e701d2de384a9b0901d5a82452d0c83dd5611ef1e11241e49dd4a1614f00c755c1358af8de7365827d51ee0399c298561d000c0225635c21" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f494241514332684a683741614c2b4f3945650a2b316e33335366725976624c3655414a784541564a6561784b5a47714e3557464a4c546a2f43722b374f6e58686f4e577262457969656e395257387a566963460a6e77584171326c6e4e45396457566771314e576a2f6b583768482f6d7569656c71796e6c4d33675a616432626e577466313542504f7475465249486a444970380a6b42784b6a6f37415436757641486e4a596a5754786c516e3647614251336d6d303836664b3879737238474a5a2f7641524d72366b75507444516f54427477560a3256737a41716f2b626f74527274304341612b6d4e493265534745784b5032744a65547850364964796b316b50456579464b693542634d7831463530506d57470a384e30767039695073657638383536633964634747516d3070766b374d696156694d384432634a512b46634c4f682b4d6d46697161774f74496e756953434a300a7176454b4239425441674d424141454367674542414a326872504e6e65586272414851716a777730453973476b4c47526c483461345747695a7a67513937347a0a335532745978455834365a68784b416769325163424e4c714b3431544833514e37777536794d5162427142636e454b352f42795a7a703267364a344f564578580a38412f7a344f345434714449507432384f6c542f48714a6536654b53525439727746744f4e7a44704b44374e554443485436647579326b4350414a76673841550a71646a4b583832524767726c636241762b70426d3043387a5853583561647a6a4f32466f6e795463664d7a344e497937774163544278572b345579724a3367470a533338357a3064497a556c654a6c5934546545784e49446247535755696170465158576e57614b35372b75503838526168784266756b6a6b65384935723946610a6643705461306e66526b79527a2f5052326548686549544d73354c36377a4347576b497a5873305265646b436759454134524c4f386f613256615644776e6f320a4167314658413636624455444737332f3652356e56626a51536f7a306b5a3063545a49572f71756e454e733048756752687a545879455376776d4f4d416337390a324130675a51594264514f546f713970634873373950424759564265647a76307a544d63444562663674764b4e794a6c44743471304b4e357a48305672772b540a584a645467526e71346e7a706867534b4d764f7a5348677270575543675945417a356a615777446e63596e496168546b4778386a6169775477496b76413839610a54674642616e72304b627147324638345035504c37516a66543474537a5147584978512f6c31794d46786950796a78615348676f7568416764646f31775677720a426b5049524c5670645a6768412b797550616f7238366941546152707447746a515453685a425836316a5634674773666c3452572f2b684c465746726b4c56640a39794c4c493848332f3163436759416a7767553236736756473069456f5433725859686a63384f535a576d3141703668767237554536536a38336a336d5770680a754a4147726562412b744a78427057695558626d7a6d4c4f453171752f742b396233687946463475355036546e694b325342336963446e2b512f4a2b4a7678680a6a586b72536964512f4e4a4871434d2b4658586635396a31372f6c39386f2b4b366b2f4e3976776d4e346f7954493559516664397a2f6d2f4d514b426744536a0a773671547668777948646d59566f627944463764736c7839374b6741716874686768376e386f57715149735a436742464438365a453162754d783638716f42410a6f436458566b5a382b656b45434c69626a326775737178766d6e586655794752696e537873333459427a63686973556b4932547a6379553837586a4d536979380a6c6266315174655355323373752b5146734c4850533071756d30486b573246524e30566e352b5364416f47415856744e4e54414463325149444c72316f4733690a766d5470512f64324963616179416c70614d445456504e625964734755566c43684775775776624556705258394c664b426d32685337667543664635556353670a6e673448626d6d65384f2f56746a53532f502f426d614b4f6d6e735047355265683144376d4f716e386757623236666e77534563574177527a696267782f79390a794f537a545546644e6a676469383979367031693875453d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100b684987b01a2fe3bd11efb59f7dd27eb62f6cbe94009c4401525e6b12991aa37958524b4e3fc2afeece9d7868356adb13289e9fd456f335627059f05c0ab6967344f5d59582ad4d5a3fe45fb847fe6ba27a5ab29e533781969dd9b9d6b5fd7904f3adb854481e30c8a7c901c4a8e8ec04fabaf0079c9623593c65427e866814379a6d3ce9f2bccacafc18967fbc044cafa92e3ed0d0a1306dc15d95b3302aa3e6e8b51aedd0201afa6348d9e48613128fdad25e4f13fa21dca4d643c47b214a8b905c331d45e743e6586f0dd2fa7d88fb1ebfcf39e9cf5d7061909b4a6f93b32269588cf03d9c250f8570b3a1f8c9858aa6b03ad227ba2482274aaf10a07d0530203010001", + "token_challenge": "0002000b497373756572204e616d65000005612c622c63", + "nonce": "0333ebcd9ac3ce34707ba613f2d423396c953899d6d792d627c78fe478174005", + "blind": "5df5f22a954465a7c5c1a00102719662bb6998fe65efdff1ddf3950a0b55f62f2877d8343973ac3de563e2aca202a57481f8be345aa3a0e2f4583f2ac80779184550651b19ec16ca77b52767467aab509fb6e98474eec7ef232212ac3424bb96cc5d500f5840fbaf8c71c697cb77d8284d2b4e861d5e2dbccf0184a0ce35223faac2fc15605d6658c311d66911ff748d46c2b6a400225c50103ed0408921929bd8ae58e48c3ff0747452278106f9d56ae7cd1483baaccc0d4ea932aed1c05edbf6c7f367e75dd6c274d16eb726376ff16ca4f187f2fea2e07d6f2fcf19fca38f41fb11d0d92dc2975a4c79c7b9bc527eedce5ee0ca55c4aff409a07c599ca153", + "salt": "9824db7a273582d75a07739ebb4fd0f9ae45e6f3433a0d5bb4fc897143c5bf9859eeaab27f4236b9d92e6f83cd3f7173", + "token_request": "0002f51dac73d46106ba7c87ea58fa101af81a0d592b86acea39e9857d9b4a72991f88e0e426216c795f93938444318e52d040ad988b8b24555799bf9f05341c6d59bf7a6652df04904bcb19f8b1fcee25753e8c86d0a07fc3af3de1c4b305d47f09caba87622a747144a9f152558bf0cee650b6da83fc00aa702d8ab794d740eaf49e12f1ecc52267547af94d81710a0d0862f7b0e4b749ef769b68596e63894123ca24dbeff5d2197a4df70ebded4833221c18a134faa6e696e6c802f9f4c4454c529b5d6fa40ce8a62e2dd04aa35bd447cd27889bedba3dda1ebd9cbd153ddd534f63d20b976192c45df02c222ec1877ef2e39b15f5b9f09dd106ddeaf42282a7b7", + "token_response": "a72a821b29ae30992691380ef69875f6581c367e5a15a145aa57e2e034aef4ef51a19b23bd7a59adc735c980e21a6edfeb55c6073198a5b7fc58e2c685c0642004d35320476085e8c430315a512613dc71996cdf49ca21b0041226976013a9dad3fe0e8eaa3cf370f986621a80b0c6f95ee0aede1a08813554758432f1783b58acd189f311a85d0029076e319a6d796333a5fe943185253f5ba58447395373a7acb65cc8b347e018eac1ab4f84d9e723426e5ae0edc3eb5552366530448dc3e0744aa9fec272f966a1d9b11beeaf5e4175d6136373c9e56e7f66fa7844b38273d9a269e22ba19c25dd6ec1a9bdcfef723e3b3271230bab249c0822c27806ae4a", + "token": "00020333ebcd9ac3ce34707ba613f2d423396c953899d6d792d627c78fe478174005201c8af95ce37c05aaa2acadc50fd6d8a825e992c5bebf2d617f0359cbb669d82ada6ee756935cd10ad4d0722985bdb485b8eff474077cee207f671e8f475bf54696ad3d41e8802ea5b33856fb00395a7141a16a0e9dc7457503aff5754d987eff0db201b74086abcd35cac4c0bf9783d8a3f6d934763dcf31b59dbdb94da80b7151542cd4b3c5e534b79367bd1b4ceffe8bff13fdda61a5cabe3259d1b0f1e5ad5adfcc29eb77c9f55c6e30d09408fbf75e3838b9f33b4ace9063fe800c7a0bd26ccda98d1fbba65600909da3a01d210a51833f731310822678b77ec38c18ab702e430cafb9e29a6d5974aa61a148f4e0dfd1a20c2d518900666a162e434c8ab8c02fde68c321add3f5ffb4939100dc152ef8da0db2a3f7a468b32c9a05f31ac7554c63c0e4e7ad08e7cfab9c10f67d3b380425851d0a32031e8e60e66ce1c8" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945766749424144414e42676b71686b6947397730424151454641415343424b67776767536b41674541416f494241514430554b46536a4f65685a4153580a3642722f7455476f495a6a5439346f61347555652f627a454335396361657a755a5177643367546332646c4f784773732f6532447462693430644654382f50670a766236624f5071676c6d494c6f736b447242643932566f306b786845576a5a414a72614a42535a6a6d6a6c742b70306a4f4c5666302f6c504e432b654e696e570a6235394a706d646b356c6f66774f6b7950485174666930716a6a474d49716573525653696442686b5375534c6e596f566f676e566f664a387456494d694d66630a376774594e33743454704f51704e487a5138556268502b76386a42374647524a474459624d6d4271744d64613269636f5356496479334a576c734f374f674c480a485454614d2f386868612b354c684c34507155695546767669354a454241707150644452372f7563304c503430796b42622b42734d476361644e5975366870520a66656e794466393141674d424141454367674542414932476c36304753726648424a4971746c6b6957714a57732f30324442352f43564c6d6b593750584d56660a357778434a4c36346567436c6761694c5237714d62695936416f4f747036454756475141426f5a4d66344b2f477053362b383751786472314b62674337612f4b0a776c707645705a564557507056784e4b6466534e5353503132786e796c524d464e54387538677273426b45413333773964396a4f424537474a4d7665437632740a673841423244366f534c726766437879335854414f3367354346585355784f7851447a386b343472674470743250554b6f517367524365595544303639414a6f0a726673584c4630454148634a45467953365a55427046335a4b4551763879473443574d714e576a4e4452716279567738354d68454f774648376e376a4444592b0a6c78636252373543343456646a6a7a2b5277494e4461532b6d345738344f6439335469552b594556643445436759454139585875667142464273524c64524c7a0a43397453584a6851325859566433494e54355049534c496f784d356673654b4b6c4f366856364a776e653246744f6b494167634c41586a58644571596565506e0a6f5756377044677a6e395149723458316e35585975686d756454694162656d434562732f6a686242596d39454161704a652f66626454442b4d75722b7643654a0a3970636e776358706e31384444546d6e305662593236584369583043675945412f733461356f4d3441454b56466d317376543032676d34774e736a6b643475580a546d2b47524176564b674f54766f50507a6c596359496269392f595646514b6738724d57576d556679616b59674f5156726a397a566e512b474a7142614142750a7934434c4b2f476661337977393463536b334e643957303844375577704e6b30503838756d736e7a3255376e4f6248336864332b7757526158466e463133357a0a6c6a614a612b673762316b43675942394e454f4c3259703845694a2f504551334e7739433566725067626b6662564332313658673175654f77386e79506144380a714f41706946557a6156573757755a58796d6e6c76564d4249592b33465059773045674b45785461494e697856333562675a374542706544655765684b4f764d0a795a743469636a6f77654b4a697566693053486a48444e396d33714756524339644971312f354c5556533245554856326858736d527433394c514b4267444b790a3738644e394b61596d50474e6445567a474a56374e7a797658455974454434624571775862714b64633568457765526358656b5737624a4931496b4a724e79640a706154323267785743506f635a2f507a7a41383373654e54577378477963545548747a7668726a735a6f6e5a356d6851584447704f6e6f6c6f7a6770763675790a6e614e4f363659686651434e324b4a614f595a6859566c657732374e536c4a704a34374139704b42416f4742414a584c4f7632305734675a4b5438574c6a6a680a4445332f78432f37784d3362333271734535527172512b4b314658342b7a777a4c51517039414c537167454b774e356978572b707630703131666a55324649700a534d59366764486f3367654a313043516a4669657364314673545352597541355579676269657237653749732b37544a48697577616e6d4946724539534855450a672b6464766b743235316a534c35497973544f656536367a0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100f450a1528ce7a1640497e81affb541a82198d3f78a1ae2e51efdbcc40b9f5c69ecee650c1dde04dcd9d94ec46b2cfded83b5b8b8d1d153f3f3e0bdbe9b38faa096620ba2c903ac177dd95a349318445a364026b6890526639a396dfa9d2338b55fd3f94f342f9e3629d66f9f49a66764e65a1fc0e9323c742d7e2d2a8e318c22a7ac4554a27418644ae48b9d8a15a209d5a1f27cb5520c88c7dcee0b58377b784e9390a4d1f343c51b84ffaff2307b14644918361b32606ab4c75ada272849521dcb725696c3bb3a02c71d34da33ff2185afb92e12f83ea522505bef8b9244040a6a3dd0d1effb9cd0b3f8d329016fe06c30671a74d62eea1a517de9f20dff750203010001", + "token_challenge": "0002000b497373756572204e616d6520b39211560f3185531482d328d0f03ab83a8525645f1cf467cca80ec8ff56a1340005612c622c63", + "nonce": "1108d87bf96102b3a895f9790851c1d2383163d6fe84e26a1f9cba6307bac23e", + "blind": "311ac19aec0342cdee732e10b7aa54c9c2d5fa5312cf8bbfd1d857ef90b4e9748238f3a36dedad4f828d6396076f62ac0c7d8af5440329cb1faf7fb78a8df599469e498fd41c8b775840c1e64e2523f08c41195e4fdd2a826b43b2638223aad5797eae96c18d645074ed75478ab8dff6cadb272a31ce49efc3bdb0020282d4e1431951314a15a0537327db5ddfeaa1459da591ac7b5b24e3dd87948ef7668cc68c15f577abc79c1f13eb4c3ada179a72d092ef4d465c059b6c09cb4f309ce9009898899a1b15a3403bcf83a622277455f6fb04d11a02f9cd7f50e9dccef30e973fdc9f97e83efa3a4c480f4ab26f88e6662e2e06850379df974d9b9cdfe76079", + "salt": "5e1406faa72f6a8711477fd6b287fd595855799ed53600d268968fd7e11bd63c4ecc19d32428aa6249b2504c0cd38a2f", + "token_request": "0002d8b05605e52b91d401b47ae7323ed3f39ba80b78b60d9ff735e76c42f98e2c1224f63ce9fea303235f1377f08df30ec3de42b4982e5b640c04138d1b85de852e6c440fedb9c48a29d6839bb8198968fb5ec18064b2cd666a50aef1680493e418c092c0c5f917a7c03a72af5edc91740f30af1630bf93a430a3c2c7bf0c05a0a3e1f65471433ed809410b6dc809962de871ff70559649cd7a0684c6f30974354b2427d637df27cdc45dc3eb0b094bedeafa80cc383ae18c27b66a56b7cc9507c1a6ad5076549084517cb9804a30318ec399ffec111da93a800d8ba18043e9a093468db55df7d9ea42ac2e11c082558b969287b7e36fb557adfc935d12ec5a1673d7", + "token_response": "73b7ca72fa502614cd010c658a11b4a7958463e695e52bd2ffd58e622eb08207ec26af21e3dbd7164626ef19a1b75c6fad3ff89cb32d2510c458b653dab3233c55de1eda9f42acaef233ff3ff11b126ed4de8dc2d999eea52516103721711844cba5956c669a9a267027a0dd2bfa8268151ccc9e23dbdf2a8f66d635ebe115563e30156838c43fdc622d3a9654f193ef47a829b43b923a304d23f234e0702c93444bd7d5c161c69347171bcc338854ea67cef931369ab66b61f7649f3432220af9645363c9a0e74c9730a1e906738bda36a74cc5dee3f88f14a2edaff4cf6996eebee7cf566edc06204227aba02703e4474d12d55ab5810ea5bd5d37c97b0de1", + "token": "00021108d87bf96102b3a895f9790851c1d2383163d6fe84e26a1f9cba6307bac23e07ec72c0f3490495ad94246c265bf6c0f19b7e4fb01521c1f3a7fb15926c855573595353a2f7484b5b51d7503fad5cb2d193d4fa54fd45c7b574845358b003d89fe5e36e8f12fc8d0e41ec21a9f83b6e0740ec2c96f9622fcbcfc6c388ce0086852b99f8fef997fdce4ae5300370da61283050fe606cbe7bf646cd887110732706672a0acfbd1d3daf3c5e788fdc96b8e58d662f2699c3bdfadd043d35177f1eb9e3973fc5558dd2827659978eae6d69264c9edd1fcc2f437377679a06bbe3a90a0b600e3684e36ad58f98d5c2fd76bf3f8576a56ef883020a3af6d4e0d0be8989aa99c71e4fe53cf8ab2f14e06947a3557eaebd9109233284862e742afbf87194e1c4c6f91ebf0b7c3b227d448b5db75f137f10e3c2e9a6e1dd33295ccfce77b65a221f3f19e405ae88ce7290186535c366722065337347c73a9f654770b124" + }, + { + "skS": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945764149424144414e42676b71686b6947397730424151454641415343424b59776767536941674541416f494241514338584e43623245706d6b35536c0a49686f696e59536d58756c626d32416e434a4d69306a4d597a525a2b38574c6375334467687649556d31633169347376773467514f585369376856636f58537a0a71385877684b4a64654755696e7535336c792f41336556545a6642654c4a396d676e347631584d392b6b4b765763625065666a59526f446474725a50716432420a593955664255384a6b57324d46326a4a414639786369686c4668704e654f73367350736a6b4a7268304e48317259465464517a727969766b3579744d4c6849760a6d796f64696541724778655647622f446f6a596972324451662b51437452617a54667452747173757333316e75646a386c374b383565656a536674414846356a0a6a4d554278446171464d644b6d6f2f2b78506a31676733506a364d68316739776a6331644c34464d2f494e4233674a7a3844534b43326842576c4a596d6646310a456d43736454707641674d424141454367674541534a792f446c546932596771315433414e58666a6d7638626c6d546650437a694335706365454d58575068550a31434b74384f41556b6a30503857524a30362f63302b5253516a7a6c6e7a2f65625354617555316f4367425066486b6f464a49447071586a73624b715a714e740a443264656e6248374b766b2b6d37705147637337557152624b52303253702b41394641333643317a756f7946586d766e7952534c746e2f37636e7943787a6f630a704c6144766444756e39496e4948724348614971413664644e41715563357850577351786573502f73566b71466351665933614741326a563652307244436e4c0a5a4952427479454f3438616e6d622f744f4e723863722f306d6f733636716156443374514a6b794c3444686c69342f652b72734d5847304a363055705a6d45300a4d675037763554434b6c7542303056594e747a77334442522f4e724e737247747a70774d4735654341514b42675144645433365844526c44694b6a7536376b4c0a76536c784a6d38547a6774397033324d484b78697150644938366762354e54664b70626245567334764c436a534d52774b534b4e7a304c5a65526b2b4e45414b0a5145396d4a456b616d596f4a5654575935484f3762566c6e614a646e47642b5561496e525971567644365139596a4f4a52514a55566b4738795356307576714f0a4e337a4b727a454b514339715852535874794e7633596a4630514b426751445a347a6833566b775730452f6373484479674c426634774c6a5344707635424c6e0a772f444451435a59617a324739352b6434756f7668456f436d796537686c57555a6754484e6f786d305736756171396d755054705a5a51694e674d734f5673750a702f4e44376649416b3149772b4a524b484969556371704c3058424c68323566747046552f3643394f6d7478526e4e344b6d63446461785859733057334f4b520a72464c646341724d50774b4267444171395370374e31787a6968453254506735473650383859416d4147704134687030777a4257426a786969397847576536590a6e6d7a66776a4c2f48704c726135586433683976564f567762675954365478523876595150376b435967445741757863444c526d674762355a4c71543777326d0a45786a336c336f73745671473353437258473575743741584e346649696e415a7335654a75657135505a697732614b4c77624b2f544e3242416f47415748674d0a43316838324a36496273457465796d4a633857472f4b58744a46566b33376f4e58686f44393837445572506b6d59547a4e59767653524f616c476539413935510a666c7246733148434d5175635949636b326b656d574357364d42437831305857586e535532477234366d316b4f4a74525559556c5a46654c307638516238682f0a526858745043676d532f346845462b584b4335315a5a33325442777632706f766f7a3346424a304367594144497a4437554b364c5747776b4e36644c6f6e626e0a3842744579576466367074364259306d567765453659486c45492b454a45714a6557667954484758714236623250585461594e2b3269747a494d622f666e7a340a5a4735654c7346796a677a48454f4955755530366f664d744655316139584966794b383175334d3768774266426f32376a317341696d746d672f50796f6f6f300a624d777363584577757047494d5055564e76436131673d3d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a", + "pkS": "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a301806092a864886f70d010108300b0609608648016503040202a2030201300382010f003082010a0282010100bc5cd09bd84a669394a5221a229d84a65ee95b9b6027089322d23318cd167ef162dcbb70e086f2149b57358b8b2fc388103974a2ee155ca174b3abc5f084a25d7865229eee77972fc0dde55365f05e2c9f66827e2fd5733dfa42af59c6cf79f8d84680ddb6b64fa9dd8163d51f054f09916d8c1768c9005f71722865161a4d78eb3ab0fb23909ae1d0d1f5ad8153750cebca2be4e72b4c2e122f9b2a1d89e02b1b179519bfc3a23622af60d07fe402b516b34dfb51b6ab2eb37d67b9d8fc97b2bce5e7a349fb401c5e638cc501c436aa14c74a9a8ffec4f8f5820dcf8fa321d60f708dcd5d2f814cfc8341de0273f0348a0b68415a525899f1751260ac753a6f0203010001", + "token_challenge": "0002000b497373756572204e616d6520c694b6f89468cdef23510c0447a13dbdd0d978b793675a2dfbcc7ce47f629faf0005612c622c63", + "nonce": "7fa3bf211b3ac9e8e4f1677ecdb5859b1add6aee94aabf580d61e35350a6a359", + "blind": "6d1e5f447b113be33a73918549b8113897768aa249d8b4184c80fe54c4b6f8e87f30912a918e9d92a7c3146682c6c78cf50f64182a49f40f9b7d681750d217d114c597500f0021fdbcebf3b69cb8d0ff85b02637623f30f1ae0b216e1ba1f9e6a30e4865914d7c7d42aeca1961fc22561acc21e52afc5a2e43ab79bf85e1868690810a77458671f05bd2fb858029506a1145bdc942b4682f1a8374fc3e4e0d3b792082136c67d1888b203b1805419f42975c24dc527a2592c7f4d5136f07f3d35d2d48eb8504772c76355700941a5f08bc8c7107f8fbd6ff6bd37f1a63b13e308dbf59f475a29f7b379f976656200a083d06d4be1b0623aa9bf946c9faac4ed5", + "salt": "ff93e0a62c4fc72eedd9d013f18699d05cfe9e0ba90e00112468020eafe2018ae343ef66aca62c11e1707c339a1f7d1d", + "token_request": "000234aef717c7a4544d856eafb04a6f6e7bdbfd4bb6e4152ca9896c404c32413eded84955c71d0c3d2542a77fdd6cdf834852d4c5eb601a773d12299e0919750dcc9f50281500c0d4e8f8defef0d5843422bee247b9634bd1ee971f82329cd767e789148c823afe741a9edc73236ab00e2db6e2f0bfccd130c3226428b8689e829c3a1a8ee20a182480dc601628d2fb022004e0dfb0c04e6bf6fbc3a797a44d197dc2ad5dbafb25abf488edc7190eed6651ccb4fec52b9014329fff863ba1b56674a8fec25b528788882eb49791ecdf1ee6fecdd700e4882f7db171365f7bbcb1dd8228f9c7ed396257c7fa5f96514471e831f8ba51635918ceb66c55c35c03687841", + "token_response": "91f19d708df804b21ba9fff31df9ceab4abc77163af1bdda8206488fce09fd398784d9d0b2131270a20732f97723957745befe91456e2a96cb1e41f91af1c61b651e908a6664fb9181e2d6d36c9111b03eadd317c20ba36ac15091acb34d21404144d112865308dc5d39b99964471d5d1389ab859bace39ac29c09f26491bda189a238f8acce9eef46c751571ad210379cb95141887b9339c75bc4754de25ce16b9a99de93214e97287554c13df7175d75de527eac5c953bd012976500f723516b6141999e1a43e9b70b18df7a9b3bf1be8262246b2f9b17695ab796cbe2d7c392fd889b72a4425131e97c11c55c167b244c7169b04597125898d3de042fd2a5", + "token": "00027fa3bf211b3ac9e8e4f1677ecdb5859b1add6aee94aabf580d61e35350a6a3593e3e8e98b916f686e16c3149056452a2f1b660906e973f0612db1c1d0296e11a0b5fbee97cdbe5cf7b8440941072631f9cada20f8729bbed728f6f85242a32349ad673e5601e823268152e62b94df754a1735ae926523e9be3e4ffc7bfd75ac53f6243556b98477e71e3e107aa3f1e89e5bdc807cf3f1369c0f708252ccb4afaadfaed6355e91a65a2e71bb3180de13a60075359a044beb4c48deaf90ae0b4d0b016d376eb5df76348209221d9cf9c63d18caa2a6a8fcd87f7f6a68fc140bb78013432846ed35c5123404544e0a265b62597f02a8ad5da268997bf1438aa38c550d2e9f494333710f097123ad5963c6506b28c5cdeaad1cdf85ce661d260808e2d624630c8afa4225e5b26c8de7bec59c7a47cdae591e03064d59ee25124e8f152a9a688125d48dd4caab4e4325c5bddf93b7b74d56f4ce2fb7525d408f0014c" + } +] \ No newline at end of file diff --git a/tests/test_data/voprf_p384_v20.json b/tests/test_data/voprf_p384_v20.json new file mode 100644 index 0000000..136be14 --- /dev/null +++ b/tests/test_data/voprf_p384_v20.json @@ -0,0 +1,24 @@ +{ + "identifier": "P384-SHA384", + "mode": 1, + "skSm": "051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992f4278f9016eafc944edaa2b43183581779d", + "pkSm": "031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b46acf59b7c0d4a9077b3da21c25dd482229a0", + "vectors": [ + { + "Input": "00", + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fca56887e564ea82653b27fdad383995ea6d02cf26d0e24d9", + "EvaluationElement": "02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d761246de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6", + "Output": "3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d026b4a622beab60220bf19078bca35a529b35c", + "Proof": "bfc6cf3859127f5fe25548859856d6b7fa1c7459f0ba5712a806fc091a3000c42d8ba34ff45f32a52e40533efd2a03bc87f3bf4f9f58028297ccb9ccb18ae7182bcd1ef239df77e3be65ef147f3acf8bc9cbfc5524b702263414f043e3b7ca2e" + }, + { + "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", + "BlindedElement": "02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e56fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b", + "EvaluationElement": "03f16f903947035400e96b7f531a38d4a07ac89a80f89d86a1bf089c525a92c7f4733729ca30c56ce78b1ab4f7d92db8b4", + "Output": "b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f", + "Proof": "d005d6daaad7571414c1e0c75f7e57f2113ca9f4604e84bc90f9be52da896fff3bee496dcde2a578ae9df315032585f801fb21c6080ac05672b291e575a40295b306d967717b28e08fcc8ad1cab47845d16af73b3e643ddcc191208e71c64630" + } + ] +} diff --git a/tests/test_http.cpp b/tests/test_http.cpp index 9ce615f..34e9aa4 100644 --- a/tests/test_http.cpp +++ b/tests/test_http.cpp @@ -4,6 +4,8 @@ #include #include +#include "test_vector_utils.hpp" + using namespace privacy_pass; using namespace privacy_pass::http; @@ -30,7 +32,8 @@ TEST_SUITE("HTTP Auth Scheme") { REQUIRE(result.has_value()); CHECK(result->challenge == "Y2hhbGxlbmdl"); - CHECK(result->token_key == "a2V5"); + REQUIRE(result->token_key.has_value()); + CHECK(*result->token_key == "a2V5"); REQUIRE(result->max_age.has_value()); CHECK(*result->max_age == 300); } @@ -42,7 +45,8 @@ TEST_SUITE("HTTP Auth Scheme") { REQUIRE(result.has_value()); CHECK(result->challenge == "Y2hhbGxlbmdl"); - CHECK(result->token_key == "a2V5"); + REQUIRE(result->token_key.has_value()); + CHECK(*result->token_key == "a2V5"); CHECK(!result->max_age.has_value()); } @@ -57,9 +61,11 @@ TEST_SUITE("HTTP Auth Scheme") { CHECK(!result.has_value()); } - SUBCASE("Missing token-key") { + SUBCASE("Missing token-key allowed") { auto result = ChallengeHeader::parse("PrivateToken challenge=\"abc\""); - CHECK(!result.has_value()); + REQUIRE(result.has_value()); + CHECK(!result->token_key.has_value()); + CHECK(!result->decode_token_key().has_value()); } } @@ -174,4 +180,40 @@ TEST_SUITE("HTTP Auth Scheme") { CHECK(MEDIA_TYPE_TOKEN_REQUEST == "application/private-token-request"); CHECK(MEDIA_TYPE_TOKEN_RESPONSE == "application/private-token-response"); } + + TEST_CASE("RFC 9577 WWW-Authenticate vectors") { + const std::array files{ + "auth_scheme_header_rfc9577.json", + "auth_scheme_header_complicated.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + auto parsed = ChallengeHeader::parse_all(vector.at("WWW-Authenticate").get()); + REQUIRE(parsed.has_value()); + + for (size_t i = 0; i < parsed->size(); ++i) { + const auto suffix = std::to_string(i); + CAPTURE(i); + + auto challenge = (*parsed)[i].decode_challenge(); + REQUIRE(challenge.has_value()); + auto challenge_bytes = challenge->serialize(); + REQUIRE(challenge_bytes.has_value()); + CHECK(*challenge_bytes == test_vectors::hex_field(vector, "token-challenge-" + suffix)); + + auto token_key = (*parsed)[i].decode_token_key(); + REQUIRE(token_key.has_value()); + CHECK(*token_key == test_vectors::hex_field(vector, "token-key-" + suffix)); + + REQUIRE((*parsed)[i].max_age.has_value()); + CHECK(*(*parsed)[i].max_age == vector.at("max-age-" + suffix).get()); + } + } + } + } } diff --git a/tests/test_issuer.cpp b/tests/test_issuer.cpp index 45de6e3..c7a54a8 100644 --- a/tests/test_issuer.cpp +++ b/tests/test_issuer.cpp @@ -7,6 +7,17 @@ using namespace privacy_pass; using namespace privacy_pass::crypto; +namespace { + +Bytes blind_message(const BlindRsaPublicKey& public_key, std::string_view message) { + auto blinding = public_key.blind( + ByteView(reinterpret_cast(message.data()), message.size())); + REQUIRE(blinding.has_value()); + return std::move(blinding->blinded_msg); +} + +} // namespace + TEST_SUITE("PublicIssuer") { TEST_CASE("Generate issuer") { auto issuer = PublicIssuer::generate(); @@ -21,7 +32,9 @@ TEST_SUITE("PublicIssuer") { auto issuer = PublicIssuer::generate(); REQUIRE(issuer.has_value()); - Bytes blinded(256, 0x42); + auto public_key = issuer->public_key(); + REQUIRE(public_key.has_value()); + auto blinded = blind_message(*public_key, "issue token"); auto request = TokenRequest::create( TokenType::BLIND_RSA, issuer->truncated_key_id(), @@ -40,7 +53,9 @@ TEST_SUITE("PublicIssuer") { auto issuer = PublicIssuer::generate(); REQUIRE(issuer.has_value()); - Bytes blinded(256, 0x42); + auto public_key = issuer->public_key(); + REQUIRE(public_key.has_value()); + auto blinded = blind_message(*public_key, "wrong key id"); auto request = TokenRequest::create( TokenType::BLIND_RSA, static_cast(issuer->truncated_key_id() + 1), // Wrong ID @@ -55,7 +70,7 @@ TEST_SUITE("PublicIssuer") { auto issuer = PublicIssuer::generate(); REQUIRE(issuer.has_value()); - Bytes blinded(97, 0x42); + Bytes blinded(49, 0x42); auto request = TokenRequest::create( TokenType::VOPRF_P384_SHA384, // Wrong type issuer->truncated_key_id(), @@ -72,7 +87,9 @@ TEST_SUITE("PublicIssuer") { BatchedTokenRequest batch; for (int i = 0; i < 5; ++i) { - Bytes blinded(256, static_cast(i)); + auto public_key = issuer->public_key(); + REQUIRE(public_key.has_value()); + auto blinded = blind_message(*public_key, "batch " + std::to_string(i)); batch.requests.push_back(TokenRequest::create( TokenType::BLIND_RSA, issuer->truncated_key_id(), @@ -133,7 +150,7 @@ TEST_SUITE("PrivateIssuer") { CHECK(response->token_type == TokenType::VOPRF_P384_SHA384); auto* voprf_resp = response->as_voprf(); REQUIRE(voprf_resp != nullptr); - CHECK(voprf_resp->evaluate_msg.size() == 97); + CHECK(voprf_resp->evaluate_msg.size() == 49); } } @@ -166,7 +183,7 @@ TEST_SUITE("MultiKeyIssuer") { REQUIRE(result.has_value()); // Issue RSA token - Bytes rsa_blinded(256, 0x11); + auto rsa_blinded = blind_message(rsa_keypair->second, "multi key rsa"); auto rsa_request = TokenRequest::create( TokenType::BLIND_RSA, rsa_truncated, std::move(rsa_blinded)); diff --git a/tests/test_moq.cpp b/tests/test_moq.cpp index eb0556e..9827f4f 100644 --- a/tests/test_moq.cpp +++ b/tests/test_moq.cpp @@ -247,6 +247,7 @@ TEST_SUITE("TokenProvider") { TokenProviderConfig config{ .max_cached_tokens = 50, .token_prefetch_threshold = std::chrono::seconds(120), + .origin_name = "origin.example.com", }; TokenProvider provider(config); diff --git a/tests/test_serialization.cpp b/tests/test_serialization.cpp index 77d36ad..5ab4019 100644 --- a/tests/test_serialization.cpp +++ b/tests/test_serialization.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: BSD-2-Clause #include #include +#include + +#include "test_vector_utils.hpp" using namespace privacy_pass; @@ -213,3 +216,69 @@ TEST_SUITE("Serialization") { CHECK(varint_size(0x40000000) == 8); } } + +TEST_SUITE("TokenResponse") { + TEST_CASE("RFC 9578 token response vectors deserialize and serialize") { + const std::array files{ + "pub_verif_rfc9578.go.json", + "pub_verif_rfc9578.rust.json", + "priv_verif_rfc9578.go.json", + "priv_verif_rfc9578.rust.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + const auto challenge_bytes = test_vectors::hex_field(vector, "token_challenge"); + const Bytes type_bytes{challenge_bytes[0], challenge_bytes[1]}; + const auto type = test_vectors::token_type_from_bytes(type_bytes); + const auto response_bytes = test_vectors::hex_field(vector, "token_response"); + + auto response = TokenResponse::deserialize(type, test_vectors::view(response_bytes)); + REQUIRE(response.has_value()); + + if (type == TokenType::BLIND_RSA) { + const auto* blind_rsa = response->as_blind_rsa(); + REQUIRE(blind_rsa != nullptr); + CHECK(blind_rsa->blind_sig == response_bytes); + } else if (type == TokenType::VOPRF_P384_SHA384) { + const auto* voprf = response->as_voprf(); + REQUIRE(voprf != nullptr); + CHECK(voprf->evaluate_msg == Bytes(response_bytes.begin(), response_bytes.begin() + 49)); + CHECK(voprf->evaluate_proof == Bytes(response_bytes.begin() + 49, response_bytes.end())); + } + + auto serialized = response->serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == response_bytes); + } + } + } + + TEST_CASE("generic batched token response vectors deserialize and serialize") { + const std::array files{ + "generic_batched_tokens_v6_go.json", + "generic_batched_tokens_v6_rs.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + const auto response_bytes = test_vectors::hex_field(vector, "token_response"); + auto response = BatchedTokenResponse::deserialize(test_vectors::view(response_bytes)); + REQUIRE(response.has_value()); + CHECK(response->responses.size() == vector.at("issuance").size()); + + auto serialized = response->serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == response_bytes); + } + } + } +} diff --git a/tests/test_token.cpp b/tests/test_token.cpp index 4fbb957..c20b94d 100644 --- a/tests/test_token.cpp +++ b/tests/test_token.cpp @@ -2,9 +2,33 @@ // SPDX-License-Identifier: BSD-2-Clause #include #include +#include + +#include "test_vector_utils.hpp" + +#include using namespace privacy_pass; +namespace { + +std::vector split_origins(const std::string& origin_info) { + std::vector origins; + size_t offset = 0; + while (offset < origin_info.size()) { + const auto comma = origin_info.find(',', offset); + if (comma == std::string::npos) { + origins.push_back(origin_info.substr(offset)); + break; + } + origins.push_back(origin_info.substr(offset, comma - offset)); + offset = comma + 1; + } + return origins; +} + +} // namespace + TEST_SUITE("Token") { TEST_CASE("Creation") { Nonce nonce{}; @@ -182,6 +206,63 @@ TEST_SUITE("Token") { CHECK(!result.has_value()); } } + + TEST_CASE("RFC 9578 token vectors deserialize and serialize") { + const std::array files{ + "pub_verif_rfc9578.go.json", + "pub_verif_rfc9578.rust.json", + "priv_verif_rfc9578.go.json", + "priv_verif_rfc9578.rust.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + const auto token_bytes = test_vectors::hex_field(vector, "token"); + auto token = Token::deserialize(test_vectors::view(token_bytes)); + REQUIRE(token.has_value()); + + auto serialized = token->serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == token_bytes); + + const auto challenge_bytes = test_vectors::hex_field(vector, "token_challenge"); + auto challenge = TokenChallenge::deserialize(test_vectors::view(challenge_bytes)); + REQUIRE(challenge.has_value()); + auto digest = challenge->digest(); + REQUIRE(digest.has_value()); + CHECK(token->challenge_digest == *digest); + } + } + } + + TEST_CASE("generic batched token vectors contain valid tokens") { + const std::array files{ + "generic_batched_tokens_v6_go.json", + "generic_batched_tokens_v6_rs.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + for (const auto& issuance : vector.at("issuance")) { + const auto token_bytes = test_vectors::hex_field(issuance, "token"); + auto token = Token::deserialize(test_vectors::view(token_bytes)); + REQUIRE(token.has_value()); + + auto serialized = token->serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == token_bytes); + } + } + } + } } TEST_SUITE("AuthenticatorInput") { @@ -214,4 +295,44 @@ TEST_SUITE("AuthenticatorInput") { AuthenticatorInput input{}; CHECK(input.serialized_size() == 2 + 32 + 32 + 32); } + + TEST_CASE("RFC 9577 token authenticator input vectors") { + const auto vectors = test_vectors::load_json("auth_scheme_token_rfc9577.json"); + + for (const auto& vector : vectors) { + CAPTURE(vector.dump()); + + const auto type = test_vectors::token_type_from_bytes( + test_vectors::hex_field(vector, "token_type")); + const auto issuer = test_vectors::bytes_to_string( + test_vectors::hex_field(vector, "issuer_name")); + const auto redemption_context = test_vectors::hex_field(vector, "redemption_context"); + const auto origin_info = test_vectors::bytes_to_string( + test_vectors::hex_field(vector, "origin_info")); + + std::optional context; + if (!redemption_context.empty()) { + context = test_vectors::fixed_bytes<32>(redemption_context); + } + + const auto challenge = TokenChallenge::create( + type, + issuer, + context, + split_origins(origin_info)); + auto challenge_digest = challenge.digest(); + REQUIRE(challenge_digest.has_value()); + + const AuthenticatorInput input{ + .token_type = type, + .nonce = test_vectors::fixed_bytes<32>(test_vectors::hex_field(vector, "nonce")), + .challenge_digest = *challenge_digest, + .token_key_id = test_vectors::fixed_bytes<32>(test_vectors::hex_field(vector, "token_key_id")), + }; + + auto serialized = input.serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == test_vectors::hex_field(vector, "token_authenticator_input")); + } + } } diff --git a/tests/test_token_request.cpp b/tests/test_token_request.cpp index 42aa392..cdd6c8a 100644 --- a/tests/test_token_request.cpp +++ b/tests/test_token_request.cpp @@ -3,6 +3,8 @@ #include #include +#include "test_vector_utils.hpp" + using namespace privacy_pass; TEST_SUITE("TokenRequest") { @@ -45,7 +47,7 @@ TEST_SUITE("TokenRequest") { } TEST_CASE("Serialization roundtrip - VOPRF") { - Bytes blinded(97, 0x33); // P-384 element size + Bytes blinded(49, 0x33); // P-384 element size auto original = TokenRequest::create( TokenType::VOPRF_P384_SHA384, @@ -61,7 +63,7 @@ TEST_SUITE("TokenRequest") { CHECK(restored->token_type == TokenType::VOPRF_P384_SHA384); CHECK(restored->truncated_token_key_id == 0x99); - CHECK(restored->blinded_msg.size() == 97); + CHECK(restored->blinded_msg.size() == 49); } TEST_CASE("Serialized size") { @@ -87,6 +89,31 @@ TEST_SUITE("TokenRequest") { CHECK(!result.has_value()); } } + + TEST_CASE("RFC 9578 token request vectors deserialize and serialize") { + const std::array files{ + "pub_verif_rfc9578.go.json", + "pub_verif_rfc9578.rust.json", + "priv_verif_rfc9578.go.json", + "priv_verif_rfc9578.rust.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + const auto request_bytes = test_vectors::hex_field(vector, "token_request"); + auto request = TokenRequest::deserialize(test_vectors::view(request_bytes)); + REQUIRE(request.has_value()); + + auto serialized = request->serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == request_bytes); + } + } + } } TEST_SUITE("BatchedTokenRequest") { @@ -139,4 +166,28 @@ TEST_SUITE("BatchedTokenRequest") { REQUIRE(serialized.has_value()); CHECK(serialized->size() == batch.serialized_size()); } + + TEST_CASE("generic batched token vectors deserialize and serialize") { + const std::array files{ + "generic_batched_tokens_v6_go.json", + "generic_batched_tokens_v6_rs.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + const auto request_bytes = test_vectors::hex_field(vector, "token_request"); + auto request = BatchedTokenRequest::deserialize(test_vectors::view(request_bytes)); + REQUIRE(request.has_value()); + CHECK(request->requests.size() == vector.at("issuance").size()); + + auto serialized = request->serialize(); + REQUIRE(serialized.has_value()); + CHECK(*serialized == request_bytes); + } + } + } } diff --git a/tests/test_types.cpp b/tests/test_types.cpp index bbf552c..c8a4150 100644 --- a/tests/test_types.cpp +++ b/tests/test_types.cpp @@ -26,7 +26,7 @@ TEST_SUITE("Types") { auto info = TokenTypeInfo::for_type(TokenType::VOPRF_P384_SHA384); CHECK(info.authenticator_size == 48); CHECK(info.key_id_size == 32); - CHECK(info.blinded_element_size == 97); + CHECK(info.blinded_element_size == 49); CHECK(info.publicly_verifiable == false); } } diff --git a/tests/test_vector_utils.hpp b/tests/test_vector_utils.hpp new file mode 100644 index 0000000..03d6da1 --- /dev/null +++ b/tests/test_vector_utils.hpp @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025 Cisco Systems +// SPDX-License-Identifier: BSD-2-Clause +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace privacy_pass::test_vectors { + +inline nlohmann::json load_json(std::string_view file_name) { + const auto path = std::filesystem::path(PRIVACY_PASS_TEST_DATA_DIR) / file_name; + std::ifstream input(path); + if (!input) { + throw std::runtime_error("failed to open test vector file: " + path.string()); + } + + nlohmann::json json; + input >> json; + return json; +} + +inline uint8_t hex_nibble(char c) { + const auto uc = static_cast(c); + if (std::isdigit(uc) != 0) { + return static_cast(c - '0'); + } + if (c >= 'a' && c <= 'f') { + return static_cast(c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') { + return static_cast(c - 'A' + 10); + } + throw std::runtime_error("invalid hex digit"); +} + +inline Bytes hex_to_bytes(std::string_view hex) { + if ((hex.size() % 2) != 0) { + throw std::runtime_error("odd-length hex string"); + } + + Bytes bytes; + bytes.reserve(hex.size() / 2); + for (size_t i = 0; i < hex.size(); i += 2) { + bytes.push_back(static_cast((hex_nibble(hex[i]) << 4) | hex_nibble(hex[i + 1]))); + } + return bytes; +} + +inline Bytes hex_field(const nlohmann::json& vector, std::string_view field) { + return hex_to_bytes(vector.at(std::string(field)).get()); +} + +template +inline std::array fixed_bytes(const Bytes& bytes) { + if (bytes.size() != N) { + throw std::runtime_error("unexpected fixed byte length"); + } + + std::array result{}; + std::copy(bytes.begin(), bytes.end(), result.begin()); + return result; +} + +inline std::string bytes_to_string(const Bytes& bytes) { + if (bytes.empty()) { + return {}; + } + return std::string(reinterpret_cast(bytes.data()), bytes.size()); +} + +inline TokenType token_type_from_bytes(const Bytes& bytes) { + if (bytes.size() != 2) { + throw std::runtime_error("token type must be two bytes"); + } + return static_cast((static_cast(bytes[0]) << 8) | bytes[1]); +} + +inline ByteView view(const Bytes& bytes) { + return ByteView(bytes.data(), bytes.size()); +} + +} // namespace privacy_pass::test_vectors diff --git a/tests/test_voprf.cpp b/tests/test_voprf.cpp index a006d96..dba5ee7 100644 --- a/tests/test_voprf.cpp +++ b/tests/test_voprf.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: BSD-2-Clause #include #include +#include + +#include "test_vector_utils.hpp" using namespace privacy_pass; using namespace privacy_pass::crypto; @@ -25,7 +28,7 @@ TEST_SUITE("VOPRF") { SUBCASE("Public key roundtrip") { auto bytes = public_key.to_bytes(); REQUIRE(bytes.has_value()); - CHECK(bytes->size() == P384_ELEMENT_SIZE); // 97 bytes compressed + CHECK(bytes->size() == P384_ELEMENT_SIZE); auto restored = VoprfPublicKey::from_bytes( ByteView(bytes->data(), bytes->size())); @@ -206,5 +209,94 @@ TEST_SUITE("VOPRF") { auto result = VoprfPrivateKey::from_bytes(ByteView{}); CHECK(!result.has_value()); } + + SUBCASE("Private key scalar equal to group order") { + const Bytes order{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, + 0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a, + 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73, + }; + + auto result = VoprfPrivateKey::from_bytes(ByteView(order.data(), order.size())); + CHECK(!result.has_value()); + } + } + + TEST_CASE("RFC 9578 private-verifiable token vectors verify") { + const std::array files{ + "priv_verif_rfc9578.go.json", + "priv_verif_rfc9578.rust.json", + }; + + for (const auto* file : files) { + const auto vectors = test_vectors::load_json(file); + for (const auto& vector : vectors) { + CAPTURE(file); + CAPTURE(vector.dump()); + + auto private_key = VoprfPrivateKey::from_bytes( + test_vectors::view(test_vectors::hex_field(vector, "skS"))); + REQUIRE(private_key.has_value()); + + auto public_key = private_key->public_key(); + REQUIRE(public_key.has_value()); + auto public_key_bytes = public_key->to_bytes(); + REQUIRE(public_key_bytes.has_value()); + CHECK(*public_key_bytes == test_vectors::hex_field(vector, "pkS")); + + const auto token_bytes = test_vectors::hex_field(vector, "token"); + auto token = Token::deserialize(test_vectors::view(token_bytes)); + REQUIRE(token.has_value()); + + VoprfServer server(std::move(*private_key)); + auto input = token->authenticator_input().serialize(); + REQUIRE(input.has_value()); + auto valid = server.verify_finalize( + test_vectors::view(*input), + test_vectors::view(token->authenticator)); + REQUIRE(valid.has_value()); + CHECK(*valid); + } + } + } + + TEST_CASE("RFC 9497 P-384 VOPRF vectors finalize") { + const auto vector_set = test_vectors::load_json("voprf_p384_v20.json"); + + auto private_key = VoprfPrivateKey::from_bytes( + test_vectors::view(test_vectors::hex_field(vector_set, "skSm"))); + REQUIRE(private_key.has_value()); + + auto public_key = private_key->public_key(); + REQUIRE(public_key.has_value()); + auto public_key_bytes = public_key->to_bytes(); + REQUIRE(public_key_bytes.has_value()); + CHECK(*public_key_bytes == test_vectors::hex_field(vector_set, "pkSm")); + + for (const auto& vector : vector_set.at("vectors")) { + CAPTURE(vector.dump()); + + auto client_key = VoprfPublicKey::from_bytes( + test_vectors::view(test_vectors::hex_field(vector_set, "pkSm"))); + REQUIRE(client_key.has_value()); + + VoprfClient client(std::move(*client_key)); + VoprfFinalizationData finalization; + finalization.blind_scalar = SecureBytes(test_vectors::view(test_vectors::hex_field(vector, "Blind"))); + finalization.blinded_element = test_vectors::hex_field(vector, "BlindedElement"); + finalization.input = test_vectors::hex_field(vector, "Input"); + + VoprfEvaluation evaluation{ + .evaluated_element = test_vectors::hex_field(vector, "EvaluationElement"), + .proof = test_vectors::hex_field(vector, "Proof"), + }; + + auto output = client.finalize(finalization, evaluation); + REQUIRE(output.has_value()); + CHECK(*output == test_vectors::hex_field(vector, "Output")); + } } }