diff --git a/Src/Fido2.Models/Fido2Configuration.cs b/Src/Fido2.Models/Fido2Configuration.cs index c5bc9c39..85abd956 100644 --- a/Src/Fido2.Models/Fido2Configuration.cs +++ b/Src/Fido2.Models/Fido2Configuration.cs @@ -126,6 +126,11 @@ public IReadOnlySet FullyQualifiedOrigins /// public CredentialBackupPolicy BackedUpCredentialPolicy { get; set; } = CredentialBackupPolicy.Allowed; +#if NET9_0_OR_GREATER + [JsonConverter(typeof(JsonStringEnumConverter))] +#else + [JsonConverter(typeof(FidoEnumConverter))] +#endif public enum CredentialBackupPolicy { /// @@ -133,9 +138,8 @@ public enum CredentialBackupPolicy /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("required")] -#else - [EnumMember(Value = "required")] #endif + [EnumMember(Value = "required")] Required, /// @@ -143,9 +147,8 @@ public enum CredentialBackupPolicy /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("allowed")] -#else - [EnumMember(Value = "allowed")] #endif + [EnumMember(Value = "allowed")] Allowed, /// @@ -153,9 +156,8 @@ public enum CredentialBackupPolicy /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("disallowed")] -#else - [EnumMember(Value = "disallowed")] #endif + [EnumMember(Value = "disallowed")] Disallowed } } diff --git a/Src/Fido2.Models/Metadata/UserVerificationMethods.cs b/Src/Fido2.Models/Metadata/UserVerificationMethods.cs index 032b6d44..21c58167 100644 --- a/Src/Fido2.Models/Metadata/UserVerificationMethods.cs +++ b/Src/Fido2.Models/Metadata/UserVerificationMethods.cs @@ -22,116 +22,103 @@ public enum UserVerificationMethods /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("presence_internal")] -#else - [EnumMember(Value = "presence_internal")] #endif + [EnumMember(Value = "presence_internal")] PRESENCE_INTERNAL = 1, /// /// This flag must be set if the authenticator uses any type of measurement of a fingerprint for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("fingerprint_internal")] -#else - [EnumMember(Value = "fingerprint_internal")] #endif + [EnumMember(Value = "fingerprint_internal")] FINGERPRINT_INTERNAL = 2, /// /// This flag must be set if the authenticator uses a local-only passcode (i.e. a passcode not known by the server) for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("passcode_internal")] -#else - [EnumMember(Value = "passcode_internal")] #endif + [EnumMember(Value = "passcode_internal")] PASSCODE_INTERNAL = 4, /// /// This flag must be set if the authenticator uses a voiceprint (also known as speaker recognition) for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("voiceprint_internal")] -#else - [EnumMember(Value = "voiceprint_internal")] #endif + [EnumMember(Value = "voiceprint_internal")] VOICEPRINT_INTERNAL = 8, /// /// This flag must be set if the authenticator uses any manner of face recognition to verify the user. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("faceprint_internal")] -#else - [EnumMember(Value = "faceprint_internal")] #endif + [EnumMember(Value = "faceprint_internal")] FACEPRINT_INTERNAL = 0x10, /// /// This flag must be set if the authenticator uses any form of location sensor or measurement for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("location_internal")] -#else - [EnumMember(Value = "location_internal")] #endif + [EnumMember(Value = "location_internal")] LOCATION_INTERNAL = 0x20, /// /// This flag must be set if the authenticator uses any form of eye biometrics for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("eyeprint_internal")] -#else - [EnumMember(Value = "eyeprint_internal")] #endif + [EnumMember(Value = "eyeprint_internal")] EYEPRINT_INTERNAL = 0x40, /// /// This flag must be set if the authenticator uses a drawn pattern for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("pattern_internal")] -#else - [EnumMember(Value = "pattern_internal")] #endif + [EnumMember(Value = "pattern_internal")] PATTERN_INTERNAL = 0x80, /// /// This flag must be set if the authenticator uses any measurement of a full hand (including palm-print, hand geometry or vein geometry) for user verification. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("handprint_internal")] -#else - [EnumMember(Value = "handprint_internal")] #endif + [EnumMember(Value = "handprint_internal")] HANDPRINT_INTERNAL = 0x100, /// /// This flag must be set if the authenticator uses a local-only passcode (i.e. a passcode not known by the server) for user verification that might be gathered outside the authenticator boundary. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("passcode_external")] -#else - [EnumMember(Value = "passcode_external")] #endif + [EnumMember(Value = "passcode_external")] PASSCODE_EXTERNAL = 0x800, /// /// This flag must be set if the authenticator uses a drawn pattern for user verification that might be gathered outside the authenticator boundary. /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("pattern_external")] -#else - [EnumMember(Value = "pattern_external")] #endif + [EnumMember(Value = "pattern_external")] PATTERN_EXTERNAL = 0x1000, /// /// This flag must be set if the authenticator will respond without any user interaction (e.g. Silent Authenticator). /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("none")] -#else - [EnumMember(Value = "none")] #endif + [EnumMember(Value = "none")] NONE = 0x200, /// /// If an authenticator sets multiple flags for user verification types, it may also set this flag to indicate that all verification methods will be enforced (e.g. faceprint AND voiceprint). If flags for multiple user verification methods are set and this flag is not set, verification with only one is necessary (e.g. fingerprint OR passcode). /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("all")] -#else - [EnumMember(Value = "all")] #endif + [EnumMember(Value = "all")] ALL = 0x400, } diff --git a/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs b/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs index ecc502d0..94459776 100644 --- a/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs +++ b/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs @@ -20,9 +20,8 @@ public enum AttestationConveyancePreference /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("none")] -#else - [EnumMember(Value = "none")] #endif + [EnumMember(Value = "none")] None, /// @@ -30,9 +29,8 @@ public enum AttestationConveyancePreference /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("indirect")] -#else - [EnumMember(Value = "indirect")] #endif + [EnumMember(Value = "indirect")] Indirect, /// @@ -40,9 +38,8 @@ public enum AttestationConveyancePreference /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("direct")] -#else - [EnumMember(Value = "direct")] #endif + [EnumMember(Value = "direct")] Direct, /// @@ -50,8 +47,7 @@ public enum AttestationConveyancePreference /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("enterprise")] -#else - [EnumMember(Value = "enterprise")] #endif + [EnumMember(Value = "enterprise")] Enterprise } diff --git a/Src/Fido2.Models/Objects/AttestationStatementFormatIdentifier.cs b/Src/Fido2.Models/Objects/AttestationStatementFormatIdentifier.cs index 3bc779cf..bdb84ee2 100644 --- a/Src/Fido2.Models/Objects/AttestationStatementFormatIdentifier.cs +++ b/Src/Fido2.Models/Objects/AttestationStatementFormatIdentifier.cs @@ -19,9 +19,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("packed")] -#else - [EnumMember(Value = "packed")] #endif + [EnumMember(Value = "packed")] Packed, /// @@ -29,9 +28,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("tpm")] -#else - [EnumMember(Value = "tpm")] #endif + [EnumMember(Value = "tpm")] Tpm, /// @@ -39,9 +37,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("android-key")] -#else - [EnumMember(Value = "android-key")] #endif + [EnumMember(Value = "android-key")] AndroidKey, /// @@ -49,9 +46,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("android-safetynet")] -#else - [EnumMember(Value = "android-safetynet")] #endif + [EnumMember(Value = "android-safetynet")] AndroidSafetyNet, /// @@ -59,9 +55,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("fido-u2f")] -#else - [EnumMember(Value = "fido-u2f")] #endif + [EnumMember(Value = "fido-u2f")] FidoU2f, /// @@ -69,9 +64,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("apple")] -#else - [EnumMember(Value = "apple")] #endif + [EnumMember(Value = "apple")] Apple, /// @@ -79,9 +73,8 @@ public enum AttestationStatementFormatIdentifier /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("none")] -#else - [EnumMember(Value = "none")] #endif + [EnumMember(Value = "none")] None } diff --git a/Src/Fido2.Models/Objects/AuthenticatorAttachment.cs b/Src/Fido2.Models/Objects/AuthenticatorAttachment.cs index 925afaa6..2cf2f9e1 100644 --- a/Src/Fido2.Models/Objects/AuthenticatorAttachment.cs +++ b/Src/Fido2.Models/Objects/AuthenticatorAttachment.cs @@ -24,9 +24,8 @@ public enum AuthenticatorAttachment /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("platform")] -#else - [EnumMember(Value = "platform")] #endif + [EnumMember(Value = "platform")] Platform, /// @@ -34,8 +33,7 @@ public enum AuthenticatorAttachment /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("cross-platform")] -#else - [EnumMember(Value = "cross-platform")] #endif + [EnumMember(Value = "cross-platform")] CrossPlatform } diff --git a/Src/Fido2.Models/Objects/AuthenticatorTransport.cs b/Src/Fido2.Models/Objects/AuthenticatorTransport.cs index 69ef45f7..9a768508 100644 --- a/Src/Fido2.Models/Objects/AuthenticatorTransport.cs +++ b/Src/Fido2.Models/Objects/AuthenticatorTransport.cs @@ -23,9 +23,8 @@ public enum AuthenticatorTransport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("usb")] -#else - [EnumMember(Value = "usb")] #endif + [EnumMember(Value = "usb")] Usb, /// @@ -33,9 +32,8 @@ public enum AuthenticatorTransport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("nfc")] -#else - [EnumMember(Value = "nfc")] #endif + [EnumMember(Value = "nfc")] Nfc, /// @@ -43,9 +41,8 @@ public enum AuthenticatorTransport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("ble")] -#else - [EnumMember(Value = "ble")] #endif + [EnumMember(Value = "ble")] Ble, /// @@ -53,9 +50,8 @@ public enum AuthenticatorTransport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("smart-card")] -#else - [EnumMember(Value = "smart-card")] #endif + [EnumMember(Value = "smart-card")] SmartCard, /// @@ -64,9 +60,8 @@ public enum AuthenticatorTransport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("hybrid")] -#else - [EnumMember(Value = "hybrid")] #endif + [EnumMember(Value = "hybrid")] Hybrid, /// @@ -75,8 +70,7 @@ public enum AuthenticatorTransport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("internal")] -#else - [EnumMember(Value = "internal")] #endif + [EnumMember(Value = "internal")] Internal, } diff --git a/Src/Fido2.Models/Objects/CredentialProtectionPolicy.cs b/Src/Fido2.Models/Objects/CredentialProtectionPolicy.cs index 294b8806..48f50262 100644 --- a/Src/Fido2.Models/Objects/CredentialProtectionPolicy.cs +++ b/Src/Fido2.Models/Objects/CredentialProtectionPolicy.cs @@ -20,9 +20,8 @@ public enum CredentialProtectionPolicy /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("userVerificationOptional")] -#else - [EnumMember(Value = "userVerificationOptional")] #endif + [EnumMember(Value = "userVerificationOptional")] UserVerificationOptional = 0x01, /// @@ -30,9 +29,8 @@ public enum CredentialProtectionPolicy /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("userVerificationOptionalWithCredentialIDList")] -#else - [EnumMember(Value = "userVerificationOptionalWithCredentialIDList")] #endif + [EnumMember(Value = "userVerificationOptionalWithCredentialIDList")] UserVerificationOptionalWithCredentialIdList = 0x02, /// @@ -40,8 +38,7 @@ public enum CredentialProtectionPolicy /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("userVerificationRequired")] -#else - [EnumMember(Value = "userVerificationRequired")] #endif + [EnumMember(Value = "userVerificationRequired")] UserVerificationRequired = 0x03 } diff --git a/Src/Fido2.Models/Objects/KeyProtection.cs b/Src/Fido2.Models/Objects/KeyProtection.cs index b3e34f25..83121766 100644 --- a/Src/Fido2.Models/Objects/KeyProtection.cs +++ b/Src/Fido2.Models/Objects/KeyProtection.cs @@ -23,9 +23,8 @@ public enum KeyProtection /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("software")] -#else - [EnumMember(Value = "software")] #endif + [EnumMember(Value = "software")] SOFTWARE = 1, /// @@ -33,9 +32,8 @@ public enum KeyProtection /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("hardware")] -#else - [EnumMember(Value = "hardware")] #endif + [EnumMember(Value = "hardware")] HARDWARE = 2, /// @@ -43,9 +41,8 @@ public enum KeyProtection /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("tee")] -#else - [EnumMember(Value = "tee")] #endif + [EnumMember(Value = "tee")] TEE = 4, /// @@ -53,9 +50,8 @@ public enum KeyProtection /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("secure_element")] -#else - [EnumMember(Value = "secure_element")] #endif + [EnumMember(Value = "secure_element")] SECURE_ELEMENT = 0x8, /// @@ -63,8 +59,7 @@ public enum KeyProtection /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("remote_handle")] -#else - [EnumMember(Value = "remote_handle")] #endif + [EnumMember(Value = "remote_handle")] REMOTE_HANDLE = 0x10, } diff --git a/Src/Fido2.Models/Objects/LargeBlobSupport.cs b/Src/Fido2.Models/Objects/LargeBlobSupport.cs index 49bcdc6d..aeca72b7 100644 --- a/Src/Fido2.Models/Objects/LargeBlobSupport.cs +++ b/Src/Fido2.Models/Objects/LargeBlobSupport.cs @@ -20,9 +20,8 @@ public enum LargeBlobSupport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("required")] -#else - [EnumMember(Value = "required")] #endif + [EnumMember(Value = "required")] Required, /// @@ -30,8 +29,7 @@ public enum LargeBlobSupport /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("preferred")] -#else - [EnumMember(Value = "preferred")] #endif + [EnumMember(Value = "preferred")] Preferred } diff --git a/Src/Fido2.Models/Objects/PublicKeyCredentialHint.cs b/Src/Fido2.Models/Objects/PublicKeyCredentialHint.cs index 073706fb..d8527245 100644 --- a/Src/Fido2.Models/Objects/PublicKeyCredentialHint.cs +++ b/Src/Fido2.Models/Objects/PublicKeyCredentialHint.cs @@ -19,9 +19,8 @@ public enum PublicKeyCredentialHint /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("security-key")] -#else - [EnumMember(Value = "security-key")] #endif + [EnumMember(Value = "security-key")] SecurityKey, /// @@ -29,9 +28,8 @@ public enum PublicKeyCredentialHint /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("client-device")] -#else - [EnumMember(Value = "client-device")] #endif + [EnumMember(Value = "client-device")] ClientDevice, /// @@ -39,8 +37,7 @@ public enum PublicKeyCredentialHint /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("hybrid")] -#else - [EnumMember(Value = "hybrid")] #endif + [EnumMember(Value = "hybrid")] Hybrid, } diff --git a/Src/Fido2.Models/Objects/PublicKeyCredentialType.cs b/Src/Fido2.Models/Objects/PublicKeyCredentialType.cs index 15e17d6a..79444548 100644 --- a/Src/Fido2.Models/Objects/PublicKeyCredentialType.cs +++ b/Src/Fido2.Models/Objects/PublicKeyCredentialType.cs @@ -16,15 +16,13 @@ public enum PublicKeyCredentialType { #if NET9_0_OR_GREATER [JsonStringEnumMemberName("public-key")] -#else - [EnumMember(Value = "public-key")] #endif + [EnumMember(Value = "public-key")] PublicKey, #if NET9_0_OR_GREATER [JsonStringEnumMemberName("invalid")] -#else - [EnumMember(Value = "invalid")] #endif + [EnumMember(Value = "invalid")] Invalid } diff --git a/Src/Fido2.Models/Objects/ResidentKeyRequirement.cs b/Src/Fido2.Models/Objects/ResidentKeyRequirement.cs index 44873540..5829ffe1 100644 --- a/Src/Fido2.Models/Objects/ResidentKeyRequirement.cs +++ b/Src/Fido2.Models/Objects/ResidentKeyRequirement.cs @@ -19,9 +19,8 @@ public enum ResidentKeyRequirement /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("required")] -#else - [EnumMember(Value = "required")] #endif + [EnumMember(Value = "required")] Required, /// @@ -29,9 +28,8 @@ public enum ResidentKeyRequirement /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("preferred")] -#else - [EnumMember(Value = "preferred")] #endif + [EnumMember(Value = "preferred")] Preferred, /// @@ -39,8 +37,7 @@ public enum ResidentKeyRequirement /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("discouraged")] -#else - [EnumMember(Value = "discouraged")] #endif + [EnumMember(Value = "discouraged")] Discouraged } diff --git a/Src/Fido2.Models/Objects/UserVerificationRequirement.cs b/Src/Fido2.Models/Objects/UserVerificationRequirement.cs index f4ae5300..2b9a4b98 100644 --- a/Src/Fido2.Models/Objects/UserVerificationRequirement.cs +++ b/Src/Fido2.Models/Objects/UserVerificationRequirement.cs @@ -21,9 +21,8 @@ public enum UserVerificationRequirement /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("required")] -#else - [EnumMember(Value = "required")] #endif + [EnumMember(Value = "required")] Required, /// @@ -32,9 +31,8 @@ public enum UserVerificationRequirement /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("preferred")] -#else - [EnumMember(Value = "preferred")] #endif + [EnumMember(Value = "preferred")] Preferred, /// @@ -43,8 +41,7 @@ public enum UserVerificationRequirement /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("discouraged")] -#else - [EnumMember(Value = "discouraged")] #endif + [EnumMember(Value = "discouraged")] Discouraged } diff --git a/Src/Fido2/AttestationFormat/MetadataAttestationType.cs b/Src/Fido2/AttestationFormat/MetadataAttestationType.cs index b1af8dfd..fd271342 100644 --- a/Src/Fido2/AttestationFormat/MetadataAttestationType.cs +++ b/Src/Fido2/AttestationFormat/MetadataAttestationType.cs @@ -18,9 +18,8 @@ internal enum MetadataAttestationType /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("basic_full")] -#else - [EnumMember(Value = "basic_full")] #endif + [EnumMember(Value = "basic_full")] ATTESTATION_BASIC_FULL = 0x3e07, /// @@ -31,9 +30,8 @@ internal enum MetadataAttestationType /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("basic_surrogate")] -#else - [EnumMember(Value = "basic_surrogate")] #endif + [EnumMember(Value = "basic_surrogate")] ATTESTATION_BASIC_SURROGATE = 0x3e08, /// @@ -41,9 +39,8 @@ internal enum MetadataAttestationType /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("ecdaa")] -#else - [EnumMember(Value = "ecdaa")] #endif + [EnumMember(Value = "ecdaa")] [Fido2Standard(Optional = true)] ATTESTATION_ECDAA = 0x3e09, @@ -52,9 +49,8 @@ internal enum MetadataAttestationType /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("attca")] -#else - [EnumMember(Value = "attca")] #endif + [EnumMember(Value = "attca")] [Fido2Standard(Optional = true)] ATTESTATION_PRIVACY_CA = 0x3e10, @@ -63,15 +59,13 @@ internal enum MetadataAttestationType /// #if NET9_0_OR_GREATER [JsonStringEnumMemberName("anonca")] -#else - [EnumMember(Value = "anonca")] #endif + [EnumMember(Value = "anonca")] ATTESTATION_ANONCA = 0x3e0c, #if NET9_0_OR_GREATER [JsonStringEnumMemberName("none")] -#else - [EnumMember(Value = "none")] #endif + [EnumMember(Value = "none")] ATTESTATION_NONE = 0x3e0b } diff --git a/Tests/Fido2.Tests/Converters/EnumSerializationTests.cs b/Tests/Fido2.Tests/Converters/EnumSerializationTests.cs new file mode 100644 index 00000000..ecce5869 --- /dev/null +++ b/Tests/Fido2.Tests/Converters/EnumSerializationTests.cs @@ -0,0 +1,170 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Fido2NetLib; +using Fido2NetLib.Objects; + +namespace Test.Converters; + +public class EnumSerializationTests +{ + private static void TestEnum(TEnum value, string expectedString) where TEnum : struct, Enum + { + // System.Text.Json + var stjSerialized = System.Text.Json.JsonSerializer.Serialize(value); + Assert.Equal($"\"{expectedString}\"", stjSerialized); + var stjDeserialized = System.Text.Json.JsonSerializer.Deserialize(stjSerialized); + Assert.Equal(value, stjDeserialized); + + // Newtonsoft.Json + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new StringEnumConverter()); + var newtonsoftSerialized = JsonConvert.SerializeObject(value, new StringEnumConverter()); + Assert.Equal($"\"{expectedString}\"", newtonsoftSerialized); + var newtonsoftDeserialized = JsonConvert.DeserializeObject(newtonsoftSerialized, new StringEnumConverter()); + Assert.Equal(value, newtonsoftDeserialized); + } + + [Theory] + [InlineData(PublicKeyCredentialType.PublicKey, "public-key")] + [InlineData(PublicKeyCredentialType.Invalid, "invalid")] + public void TestPublicKeyCredentialType(PublicKeyCredentialType value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(UserVerificationMethods.PRESENCE_INTERNAL, "presence_internal")] + [InlineData(UserVerificationMethods.FINGERPRINT_INTERNAL, "fingerprint_internal")] + [InlineData(UserVerificationMethods.PASSCODE_INTERNAL, "passcode_internal")] + [InlineData(UserVerificationMethods.VOICEPRINT_INTERNAL, "voiceprint_internal")] + [InlineData(UserVerificationMethods.FACEPRINT_INTERNAL, "faceprint_internal")] + [InlineData(UserVerificationMethods.LOCATION_INTERNAL, "location_internal")] + [InlineData(UserVerificationMethods.EYEPRINT_INTERNAL, "eyeprint_internal")] + [InlineData(UserVerificationMethods.PATTERN_INTERNAL, "pattern_internal")] + [InlineData(UserVerificationMethods.HANDPRINT_INTERNAL, "handprint_internal")] + [InlineData(UserVerificationMethods.PASSCODE_EXTERNAL, "passcode_external")] + [InlineData(UserVerificationMethods.PATTERN_EXTERNAL, "pattern_external")] + [InlineData(UserVerificationMethods.NONE, "none")] + [InlineData(UserVerificationMethods.ALL, "all")] + public void TestUserVerificationMethods(UserVerificationMethods value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(AttestationStatementFormatIdentifier.Packed, "packed")] + [InlineData(AttestationStatementFormatIdentifier.Tpm, "tpm")] + [InlineData(AttestationStatementFormatIdentifier.AndroidKey, "android-key")] + [InlineData(AttestationStatementFormatIdentifier.AndroidSafetyNet, "android-safetynet")] + [InlineData(AttestationStatementFormatIdentifier.FidoU2f, "fido-u2f")] + [InlineData(AttestationStatementFormatIdentifier.Apple, "apple")] + [InlineData(AttestationStatementFormatIdentifier.None, "none")] + public void TestAttestationStatementFormatIdentifier(AttestationStatementFormatIdentifier value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(AuthenticatorTransport.Usb, "usb")] + [InlineData(AuthenticatorTransport.Nfc, "nfc")] + [InlineData(AuthenticatorTransport.Ble, "ble")] + [InlineData(AuthenticatorTransport.SmartCard, "smart-card")] + [InlineData(AuthenticatorTransport.Hybrid, "hybrid")] + [InlineData(AuthenticatorTransport.Internal, "internal")] + public void TestAuthenticatorTransport(AuthenticatorTransport value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(KeyProtection.SOFTWARE, "software")] + [InlineData(KeyProtection.HARDWARE, "hardware")] + [InlineData(KeyProtection.TEE, "tee")] + [InlineData(KeyProtection.SECURE_ELEMENT, "secure_element")] + [InlineData(KeyProtection.REMOTE_HANDLE, "remote_handle")] + public void TestKeyProtection(KeyProtection value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(AttestationConveyancePreference.None, "none")] + [InlineData(AttestationConveyancePreference.Indirect, "indirect")] + [InlineData(AttestationConveyancePreference.Direct, "direct")] + [InlineData(AttestationConveyancePreference.Enterprise, "enterprise")] + public void TestAttestationConveyancePreference(AttestationConveyancePreference value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(CredentialProtectionPolicy.UserVerificationOptional, "userVerificationOptional")] + [InlineData(CredentialProtectionPolicy.UserVerificationOptionalWithCredentialIdList, "userVerificationOptionalWithCredentialIDList")] + [InlineData(CredentialProtectionPolicy.UserVerificationRequired, "userVerificationRequired")] + public void TestCredentialProtectionPolicy(CredentialProtectionPolicy value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(PublicKeyCredentialHint.SecurityKey, "security-key")] + [InlineData(PublicKeyCredentialHint.ClientDevice, "client-device")] + [InlineData(PublicKeyCredentialHint.Hybrid, "hybrid")] + public void TestPublicKeyCredentialHint(PublicKeyCredentialHint value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(ResidentKeyRequirement.Required, "required")] + [InlineData(ResidentKeyRequirement.Preferred, "preferred")] + [InlineData(ResidentKeyRequirement.Discouraged, "discouraged")] + public void TestResidentKeyRequirement(ResidentKeyRequirement value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(UserVerificationRequirement.Required, "required")] + [InlineData(UserVerificationRequirement.Preferred, "preferred")] + [InlineData(UserVerificationRequirement.Discouraged, "discouraged")] + public void TestUserVerificationRequirement(UserVerificationRequirement value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(AuthenticatorAttachment.Platform, "platform")] + [InlineData(AuthenticatorAttachment.CrossPlatform, "cross-platform")] + public void TestAuthenticatorAttachment(AuthenticatorAttachment value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(LargeBlobSupport.Required, "required")] + [InlineData(LargeBlobSupport.Preferred, "preferred")] + public void TestLargeBlobSupport(LargeBlobSupport value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(Fido2Configuration.CredentialBackupPolicy.Required, "required")] + [InlineData(Fido2Configuration.CredentialBackupPolicy.Allowed, "allowed")] + [InlineData(Fido2Configuration.CredentialBackupPolicy.Disallowed, "disallowed")] + public void TestCredentialBackupPolicy(Fido2Configuration.CredentialBackupPolicy value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(AuthenticatorStatus.NOT_FIDO_CERTIFIED, "NOT_FIDO_CERTIFIED")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED, "FIDO_CERTIFIED")] + [InlineData(AuthenticatorStatus.USER_VERIFICATION_BYPASS, "USER_VERIFICATION_BYPASS")] + [InlineData(AuthenticatorStatus.ATTESTATION_KEY_COMPROMISE, "ATTESTATION_KEY_COMPROMISE")] + [InlineData(AuthenticatorStatus.USER_KEY_REMOTE_COMPROMISE, "USER_KEY_REMOTE_COMPROMISE")] + [InlineData(AuthenticatorStatus.USER_KEY_PHYSICAL_COMPROMISE, "USER_KEY_PHYSICAL_COMPROMISE")] + [InlineData(AuthenticatorStatus.UPDATE_AVAILABLE, "UPDATE_AVAILABLE")] + [InlineData(AuthenticatorStatus.REVOKED, "REVOKED")] + [InlineData(AuthenticatorStatus.SELF_ASSERTION_SUBMITTED, "SELF_ASSERTION_SUBMITTED")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED_L1, "FIDO_CERTIFIED_L1")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED_L1plus, "FIDO_CERTIFIED_L1plus")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED_L2, "FIDO_CERTIFIED_L2")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED_L2plus, "FIDO_CERTIFIED_L2plus")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED_L3, "FIDO_CERTIFIED_L3")] + [InlineData(AuthenticatorStatus.FIDO_CERTIFIED_L3plus, "FIDO_CERTIFIED_L3plus")] + public void TestAuthenticatorStatus(AuthenticatorStatus value, string expected) => TestEnum(value, expected); + + [Theory] + [InlineData(COSE.Algorithm.ES256, "-7")] + [InlineData(COSE.Algorithm.RS256, "-257")] + public void TestCOSEAlgorithm(COSE.Algorithm value, string expected) + { + // COSE algorithms are currently serialized as numbers in STJ + var stjSerialized = System.Text.Json.JsonSerializer.Serialize(value); + Assert.Equal(expected, stjSerialized); + var stjDeserialized = System.Text.Json.JsonSerializer.Deserialize(stjSerialized); + Assert.Equal(value, stjDeserialized); + + // Newtonsoft.Json (with StringEnumConverter) serializes them as strings if we use the converter, + // but since they don't have [EnumMember], it uses the member name. + var newtonsoftSerialized = JsonConvert.SerializeObject(value, new StringEnumConverter()); + Assert.Equal($"\"{(value.ToString())}\"", newtonsoftSerialized); + } + + [Theory] + [InlineData(COSE.EllipticCurve.P256, "1")] + [InlineData(COSE.EllipticCurve.Ed25519, "6")] + public void TestCOSEEllipticCurve(COSE.EllipticCurve value, string expected) + { + var stjSerialized = System.Text.Json.JsonSerializer.Serialize(value); + Assert.Equal(expected, stjSerialized); + var stjDeserialized = System.Text.Json.JsonSerializer.Deserialize(stjSerialized); + Assert.Equal(value, stjDeserialized); + + var newtonsoftSerialized = JsonConvert.SerializeObject(value, new StringEnumConverter()); + Assert.Equal($"\"{(value.ToString())}\"", newtonsoftSerialized); + } +}