PostHandshakeResponse::Options supportedOptions)
: CtapCommand(Method::MAKE_CREDENTIAL, clientData, std::move(supportedOptions)), m_options(options)
{
+ if (EffectiveRK() && !m_supportedOptions.rk)
+ THROW_UNSUPPORTED("Authenticator does not support requested discoverable credentials");
+}
+
+bool MakeCredentialCommand::EffectiveRK() const noexcept
+{
+ if (!m_options.authenticator_selection)
+ return false;
+
+ const auto &selection = *m_options.authenticator_selection;
+ return (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED) ||
+ (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED &&
+ m_supportedOptions.rk) ||
+ (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE &&
+ selection.require_resident_key);
}
void MakeCredentialCommand::Serialize(Buffer &output) const
auto optionsMap = map.OpenMapAt(KEY_MC_CMD_OPTIONS, 2);
const auto &selection = *m_options.authenticator_selection;
- auto effectiveRk =
- (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED) ||
- (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED &&
- m_supportedOptions.rk) ||
- (selection.resident_key == WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE &&
- selection.require_resident_key);
+ auto effectiveRk = EffectiveRK();
+
optionsMap.AppendBooleanAt("rk", effectiveRk);
// TODO user presence "up" not mapped?
PostHandshakeResponse::Options supportedOptions)
: CtapCommand(Method::GET_ASSERTION, clientData, std::move(supportedOptions)), m_options(options)
{
+ if (!m_options.allow_credentials && !m_supportedOptions.rk)
+ THROW_UNSUPPORTED("Authenticator does not support discoverable credentials."
+ "'allow_credentials' must not be empty.");
}
void GetAssertionCommand::Serialize(Buffer &output) const
credentials = nullptr;
}
+constexpr unsigned char CLIENT_DATA_JSON_DATA[] = "\x00\x01\x02\x03";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_const_buffer_s CLIENT_DATA_JSON{CLIENT_DATA_JSON_DATA, sizeof(CLIENT_DATA_JSON_DATA) - 1};
+constexpr wauthn_client_data_s CLIENT_DATA{&CLIENT_DATA_JSON, WAUTHN_HASH_ALGORITHM_SHA_256};
+
+constexpr char RP_NAME[] = "Rp name";
+constexpr char RP_ID[] = "Rp.id";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_rp_entity_s RP{RP_NAME, RP_ID};
+
+constexpr unsigned char USER_ID_DATA[] = "\x00\x01\x02\x03\x04\x05\x06\x07";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_const_buffer_s USER_ID{USER_ID_DATA, sizeof(USER_ID_DATA) - 1};
+
+constexpr char USER_NAME[] = "User name";
+constexpr char USER_DISPLAY_NAME[] = "User display name";
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_user_entity_s USER{USER_NAME, &USER_ID, USER_DISPLAY_NAME};
+
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_pubkey_cred_param_s PUBKEY_PARAM[] = {
+ {WAUTHN_PUBKEY_CRED_TYPE_PUBLIC_KEY, WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256}
+};
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+wauthn_pubkey_cred_params_s PUBKEY_PARAMS{1, PUBKEY_PARAM};
+
} // namespace
TEST(Messages, ParsePostHandshakeMessage1)
TEST(Messages, SerializeMakeCredential1)
{
- unsigned char client_data_json_data[] = "\x00\x01\x02\x03";
- wauthn_const_buffer_s client_data_json;
- client_data_json.data = client_data_json_data;
- client_data_json.size = sizeof(client_data_json_data) - 1;
- wauthn_client_data_s client_data;
- client_data.hash_alg = WAUTHN_HASH_ALGORITHM_SHA_256;
- client_data.client_data_json = &client_data_json;
-
- wauthn_rp_entity_s rp;
- char rp_name[] = "Rp name";
- rp.name = rp_name;
- char rp_id[] = "Rp.id";
- rp.id = rp_id;
-
- wauthn_const_buffer_s user_id;
- unsigned char user_id_data[] = "\x00\x01\x02\x03\x04\x05\x06\x07";
- user_id.data = user_id_data;
- user_id.size = sizeof(user_id_data) - 1;
- wauthn_user_entity_s user;
- char user_name[] = "User name";
- user.name = user_name;
- user.id = &user_id;
- char user_display_name[] = "User display name";
- user.display_name = user_display_name;
wauthn_pubkey_cred_params_s pubkey_cred_params;
constexpr std::array ALGS = {WAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256,
pubkey_cred_params.size = ALGS.size();
pubkey_cred_params.params = params;
wauthn_pubkey_cred_creation_options_s options;
- options.rp = &rp;
- options.user = &user;
+ options.rp = &RP;
+ options.user = &USER;
options.pubkey_cred_params = &pubkey_cred_params;
options.exclude_credentials = nullptr;
- options.authenticator_selection = nullptr; // TODO
+ options.authenticator_selection = nullptr;
options.hints = nullptr;
options.attestation = WAUTHN_ATTESTATION_PREF_NONE;
options.attestation_formats = nullptr;
// serialize MC command
Buffer buffer;
- const MakeCredentialCommand msg(client_data, options, {false, false});
+ const MakeCredentialCommand msg(CLIENT_DATA, options, {false, false});
ASSERT_NO_THROW(msg.Serialize(buffer));
// serialize MC command
Buffer buffer;
- const MakeCredentialCommand msg(client_data, options, {false, false});
+ const MakeCredentialCommand msg(client_data, options, {true, false});
ASSERT_NO_THROW(msg.Serialize(buffer));
EXPECT_EQ(ToBufferView(buffer),
TestCredentials(msg, options.exclude_credentials);
}
+TEST(Messages, MakeCredentialDiscoverableCredentials)
+{
+ wauthn_pubkey_cred_creation_options_s options;
+ options.rp = &RP;
+ options.user = &USER;
+ options.pubkey_cred_params = &PUBKEY_PARAMS;
+ options.exclude_credentials = nullptr;
+ options.authenticator_selection = nullptr;
+ options.hints = nullptr;
+ options.attestation = WAUTHN_ATTESTATION_PREF_NONE;
+ options.attestation_formats = nullptr;
+ options.extensions = nullptr;
+ options.linked_device = nullptr;
+
+ EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {false, false}));
+ EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {true, false}));
+
+ wauthn_authenticator_sel_cri_s authenticator_selection{
+ WAUTHN_AUTHENTICATOR_ATTACHMENT_NONE,
+ WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE,
+ false,
+ WAUTHN_USER_VERIFICATION_REQUIREMENT_NONE};
+ options.authenticator_selection = &authenticator_selection;
+
+ auto testDiscoverable =
+ [&](bool requireRK, wauthn_resident_key_requirement_e requirement, bool throws) {
+ authenticator_selection.require_resident_key = requireRK;
+ authenticator_selection.resident_key = requirement;
+ EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {true, false}));
+ if (throws)
+ EXPECT_THROW(MakeCredentialCommand(CLIENT_DATA, options, {false, false}),
+ Exception::NotSupported);
+ else
+ EXPECT_NO_THROW(MakeCredentialCommand(CLIENT_DATA, options, {false, false}));
+ };
+
+ testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE, false);
+ testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED, false);
+ testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED, false);
+ testDiscoverable(false, WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED, true);
+ testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_NONE, true);
+ testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED, false);
+ testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED, false);
+ testDiscoverable(true, WAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED, true);
+}
+
TEST(Messages, SerializeGetAssertion)
{
unsigned char client_data_json_data[] =
TestCredentials(msg, options.allow_credentials);
}
+TEST(Messages, GetAssertionDiscoverableCredentials)
+{
+ wauthn_pubkey_cred_request_options_s options;
+ options.timeout = 0; // not serialized
+ options.rpId = RP_ID;
+ options.allow_credentials = nullptr;
+ options.user_verification = WAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
+ options.hints = nullptr;
+ options.attestation = WAUTHN_ATTESTATION_PREF_NONE;
+ options.attestation_formats = nullptr;
+ options.extensions = nullptr;
+ options.linked_device = nullptr;
+
+ EXPECT_THROW(GetAssertionCommand(CLIENT_DATA, options, {false, false}),
+ Exception::NotSupported);
+ EXPECT_NO_THROW(GetAssertionCommand(CLIENT_DATA, options, {true, false}));
+
+ wauthn_pubkey_cred_descriptors_s pubkeyCredDescriptors{1, &GetPubkeyDescriptor()};
+ options.allow_credentials = &pubkeyCredDescriptors;
+ EXPECT_NO_THROW(GetAssertionCommand(CLIENT_DATA, options, {false, false}));
+ EXPECT_NO_THROW(GetAssertionCommand(CLIENT_DATA, options, {true, false}));
+}
+
TEST(Messages, ShutdownMessage)
{
const ShutdownMessage shutdown;