-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Android-safetynet attestation format.
- Loading branch information
1 parent
2fd2834
commit 065784e
Showing
9 changed files
with
339 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 2 additions & 1 deletion
3
...ebAuthentication.WasmExample/KristofferStrube.Blazor.WebAuthentication.WasmExample.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
...be.Blazor.WebAuthentication/AttestationStatements/AndroidSafetyNetAttestationStatement.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using System.Formats.Cbor; | ||
|
||
namespace KristofferStrube.Blazor.WebAuthentication; | ||
|
||
/// <summary> | ||
/// When the authenticator is a platform authenticator on certain Android platforms, the attestation statement may be based on the SafetyNet API. | ||
/// In this case the authenticator data is completely controlled by the caller of the SafetyNet API (typically an application running on the Android platform) | ||
/// and the attestation statement provides some statements about the health of the platform and the identity of the calling application | ||
/// </summary> | ||
/// <remarks><see href="https://www.w3.org/TR/webauthn-3/#sctn-android-safetynet-attestation">See the API definition here</see>.</remarks> | ||
public class AndroidSafetyNetAttestationStatement : AttestationStatement | ||
{ | ||
/// <summary> | ||
/// The algorithm used to generate the attestation signature. | ||
/// </summary> | ||
public required string Version { get; set; } | ||
|
||
/// <summary> | ||
/// The UTF-8 encoded result of the <c>getJwsResult()</c> call of the SafetyNet API. | ||
/// This value is a JWS object in Compact Serialization. | ||
/// </summary> | ||
public required byte[] Response { get; set; } | ||
|
||
internal static AndroidSafetyNetAttestationStatement ReadAttestationStatement(CborReader cborReader) | ||
{ | ||
CborReaderState state = cborReader.PeekState(); | ||
if (state is not CborReaderState.TextString) | ||
{ | ||
throw new FormatException($"Attestation Statement's second key was of type '{state}' but '{CborReaderState.TextString}' was expected."); | ||
} | ||
|
||
string label = cborReader.ReadTextString(); | ||
if (label is not "attStmt") | ||
{ | ||
throw new FormatException($"Attestation Statement's second key was '{label}' but 'attStmt' was expected."); | ||
} | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.StartMap) | ||
{ | ||
throw new FormatException($"Attestation Statement's 'attStmt' was of type '{state}' but '{CborReaderState.StartMap}' was expected."); | ||
} | ||
|
||
int? mapSize = cborReader.ReadStartMap(); | ||
if (mapSize is not 2) | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format had '{mapSize}' entries but '2' was expected."); | ||
} | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.TextString) | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format's first key was of type '{state}' but '{CborReaderState.TextString}' was expected."); | ||
} | ||
|
||
label = cborReader.ReadTextString(); | ||
if (label is not "ver") | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format's first key was '{label}' but 'ver' was expected."); | ||
} | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.NegativeInteger) | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format's 'ver' was of type '{state}' but '{CborReaderState.TextString}' was expected."); | ||
} | ||
|
||
string version = cborReader.ReadTextString(); | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.TextString) | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format's second key was of type '{state}' but '{CborReaderState.TextString}' was expected."); | ||
} | ||
|
||
label = cborReader.ReadTextString(); | ||
if (label is not "response") | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format's second key was '{label}' but 'response' was expected."); | ||
} | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.ByteString) | ||
{ | ||
throw new FormatException($"Attestation Statement's packed format's 'response' was of type '{state}' but '{CborReaderState.ByteString}' was expected."); | ||
} | ||
|
||
byte[] response = cborReader.ReadByteString(); | ||
|
||
return new() | ||
{ | ||
Version = version, | ||
Response = response | ||
}; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/KristofferStrube.Blazor.WebAuthentication/AttestationStatements/AttestationStatement.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using System.Formats.Cbor; | ||
|
||
namespace KristofferStrube.Blazor.WebAuthentication; | ||
|
||
/// <summary> | ||
/// WebAuthn supports pluggable attestation statement formats. | ||
/// Attestation statement formats are identified by a string, called an attestation statement format identifier, chosen by the author of the attestation statement format. | ||
/// </summary> | ||
/// <remarks><see href="https://www.w3.org/TR/webauthn-3/#sctn-defined-attestation-formats">See the API definition here</see>.</remarks> | ||
public abstract class AttestationStatement | ||
{ | ||
public static AttestationStatement ReadFromBase64EncodedAttestationStatement(string input) | ||
{ | ||
CborReader cborReader = new(Convert.FromBase64String(input)); | ||
|
||
CborReaderState state = cborReader.PeekState(); | ||
|
||
if (state is not CborReaderState.StartMap) | ||
{ | ||
throw new FormatException("Attestation Statement did not start with a map."); | ||
} | ||
|
||
int? mapSize = cborReader.ReadStartMap(); | ||
if (mapSize is not 3) | ||
{ | ||
throw new FormatException($"Attestation Statement had '{mapSize}' entries in its first map but '3' was expected."); | ||
} | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.TextString) | ||
{ | ||
throw new FormatException($"Attestation Statement's first key was of type '{state}' but '{CborReaderState.TextString}' was expected."); | ||
} | ||
|
||
string label = cborReader.ReadTextString(); | ||
if (label is not "fmt") | ||
{ | ||
throw new FormatException($"Attestation Statement's first key was '{label}' but 'fmt' was expected."); | ||
} | ||
|
||
state = cborReader.PeekState(); | ||
if (state is not CborReaderState.TextString) | ||
{ | ||
throw new FormatException($"Attestation Statement's first value was of type '{state}' but '{CborReaderState.TextString}' was expected."); | ||
} | ||
|
||
string fmt = cborReader.ReadTextString(); | ||
|
||
return fmt switch | ||
{ | ||
"packed" => PackedAttestationStatement.ReadAttestationStatement(cborReader), | ||
"android-safetynet" => AndroidSafetyNetAttestationStatement.ReadAttestationStatement(cborReader), | ||
_ => throw new FormatException($"Attestation Statement had format '{fmt}' which was not supported.") | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.