Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Add credProtect extension to Fido2.Models #448

Merged
merged 1 commit into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions Src/Fido2.Models/Converters/FidoEnumConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,26 @@ public sealed class FidoEnumConverter<[DynamicallyAccessedMembers(DynamicallyAcc
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string text = reader.GetString();

if (EnumNameMapper<T>.TryGetValue(reader.GetString(), out T value))
{
return value;
}
else
switch (reader.TokenType)
{
throw new JsonException($"Invalid enum value = {text}");
case JsonTokenType.String:
string text = reader.GetString();
if (EnumNameMapper<T>.TryGetValue(text, out T value))
return value;
else
throw new JsonException($"Invalid enum value = \"{text}\"");

case JsonTokenType.Number:
if (!reader.TryGetInt32(out var number))
throw new JsonException($"Invalid enum value = {reader.GetString()}");
var casted = (T)(object)number; // ints can always be casted to enum, even when the value is not defined
if (Enum.IsDefined(casted))
return casted;
else
throw new JsonException($"Invalid enum value = {number}");

default:
throw new JsonException($"Invalid enum value ({reader.TokenType})");
}
}

Expand Down
22 changes: 22 additions & 0 deletions Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,27 @@ public sealed class AuthenticationExtensionsClientInputs
[JsonPropertyName("prf")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public AuthenticationExtensionsPRFInputs? PRF { get; set; }

/// <summary>
/// This registration extension allows relying parties to specify a credential protection policy when creating a credential.
/// Additionally, authenticators MAY choose to establish a default credential protection policy greater than <c>UserVerificationOptional</c> (the lowest level)
/// and unilaterally enforce such policy. Authenticators not supporting some form of user verification MUST NOT support this extension.
/// Authenticators supporting some form of user verification MUST process this extension and persist the credProtect value with the credential,
/// even if the authenticator is not protected by some form of user verification at the time.
/// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension
/// </summary>
[JsonPropertyName("credentialProtectionPolicy")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public CredentialProtectionPolicy? CredentialProtectionPolicy { get; set; }

/// <summary>
/// This controls whether it is better to fail to create a credential rather than ignore the protection policy.
/// When true, and <c>CredentialProtectionPolicy</c>'s value is
/// either <c>UserVerificationOptionalWithCredentialIdList</c> or <c>UserVerificationRequired</c>, the platform
/// SHOULD NOT create the credential in a way that does not implement the requested protection policy.
/// </summary>
[JsonPropertyName("enforceCredentialProtectionPolicy")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? EnforceCredentialProtectionPolicy { get; set; }
}

Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,13 @@ public class AuthenticationExtensionsClientOutputs
[JsonPropertyName("prf")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public AuthenticationExtensionsPRFOutputs? PRF { get; set; }


/// <summary>
/// The <c>CredentialProtectionPolicy</c> stored alongside the created credential
/// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension
/// </summary>
[JsonPropertyName("credProtect")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public CredentialProtectionPolicy? CredProtect { get; set; }
}
31 changes: 31 additions & 0 deletions Src/Fido2.Models/Objects/CredentialProtectionPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace Fido2NetLib.Objects;

/// <summary>
/// CredentialProtectionPolicy
/// https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension
/// </summary>
[JsonConverter(typeof(FidoEnumConverter<CredentialProtectionPolicy>))]
public enum CredentialProtectionPolicy
{
/// <summary>
/// This reflects "FIDO_2_0" semantics. In this configuration, performing some form of user verification is OPTIONAL with or without credentialID list.
/// This is the default state of the credential if the extension is not specified
/// </summary>
[EnumMember(Value = "userVerificationOptional")]
UserVerificationOptional = 0x01,

/// <summary>
/// In this configuration, credential is discovered only when its credentialID is provided by the platform or when some form of user verification is performed.
/// </summary>
[EnumMember(Value = "userVerificationOptionalWithCredentialIDList")]
UserVerificationOptionalWithCredentialIdList = 0x02,

/// <summary>
/// TThis reflects that discovery and usage of the credential MUST be preceded by some form of user verification.
/// </summary>
[EnumMember(Value = "userVerificationRequired")]
UserVerificationRequired = 0x03
}
8 changes: 8 additions & 0 deletions Test/Converters/FidoEnumConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ public void CorrectlyFallsBackToMemberName()
Assert.Equal(ABC.A, JsonSerializer.Deserialize<ABC>("\"a\""));
}

[Fact]
public void CorrectlyDeserializesNumericEnumValue()
{
Assert.Equal(CredentialProtectionPolicy.UserVerificationRequired, JsonSerializer.Deserialize<CredentialProtectionPolicy>($"{CredentialProtectionPolicy.UserVerificationRequired:d}"));
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<CredentialProtectionPolicy>($"99"));
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<CredentialProtectionPolicy>($"99.7"));
}

[Fact]
public void DeserializationIsCaseInsensitive()
{
Expand Down