Skip to content
Merged
3 changes: 3 additions & 0 deletions src/WalletFramework.Core/Functional/OptionFun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ public static Option<IEnumerable<T>> AsOption<T>(this IEnumerable<T> enumerable)
// ReSharper disable once PossibleMultipleEnumeration
: Some(enumerable);

public static Option<T> AsOption<T>(this T? value) where T : class =>
value != null ? Option<T>.Some(value) : Option<T>.None;

public static T UnwrapOrThrow<T>(this Option<T> option, Exception e) =>
option.Match(
t => t,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,31 @@
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using WalletFramework.Core.Base64Url;
using WalletFramework.Core.Functional;
using WalletFramework.Oid4Vc.Oid4Vp.Jwk;
using WalletFramework.Oid4Vc.Oid4Vp.Models;

namespace WalletFramework.Oid4Vc.Oid4Vp.AuthResponse.Encryption;

public record EncryptedAuthorizationResponse(string Jwe, Option<string> State)
public record EncryptedAuthorizationResponse(string Jwe)
{
public override string ToString() => Jwe;
}

public static class EncryptedAuthorizationResponseFun
{
private static readonly IReadOnlyDictionary<string, JweEncryption> SupportedEncAlgorithmsMap = new Dictionary<string, JweEncryption>
{
["A256GCM"] = JweEncryption.A256GCM,
["A128CBC-HS256"] = JweEncryption.A128CBC_HS256
};

const string DefaultEncAlgorithm = "A256GCM";

public static EncryptedAuthorizationResponse Encrypt(
this AuthorizationResponse response,
JsonWebKey verifierPubKey,
string apv,
Option<string> authorizationEncryptedResponseEnc,
Option<string[]> encryptedResponseEncAlgorithms,
Option<Nonce> mdocNonce)
{
var apvBase64 = Base64UrlString.CreateBase64UrlString(apv.GetUTF8Bytes());
Expand All @@ -44,20 +51,20 @@ public static EncryptedAuthorizationResponse Encrypt(
var settings = new JwtSettings();
settings.RegisterJwe(JweEncryption.A256GCM, new AesGcmEncryption());

var selectedEncAlgorithm = encryptedResponseEncAlgorithms.Match(
encAlgs => encAlgs.FirstOrDefault(encAlg => SupportedEncAlgorithmsMap.ContainsKey(encAlg))
?? throw new NotSupportedException("Unsupported response encryption algorithms requested by verifier."),
() => DefaultEncAlgorithm);

var jwe = JWE.EncryptBytes(
response.ToJson().GetUTF8Bytes(),
[new JweRecipient(JweAlgorithm.ECDH_ES, verifierPubKey.ToEcdh())],
authorizationEncryptedResponseEnc.ToNullable() switch {
"A256GCM" => JweEncryption.A256GCM,
"A128CBC-HS256" => JweEncryption.A128CBC_HS256,
null => JweEncryption.A256GCM,
_ => throw new NotSupportedException("Unsupported response encryption algorithm requested by verifier.")
},
SupportedEncAlgorithmsMap[selectedEncAlgorithm],
mode: SerializationMode.Compact,
extraProtectedHeaders: headers,
settings: settings);

return new EncryptedAuthorizationResponse(jwe, response.State);
return new EncryptedAuthorizationResponse(jwe);
}

public static FormUrlEncodedContent ToFormUrl(this EncryptedAuthorizationResponse response)
Expand All @@ -67,8 +74,6 @@ public static FormUrlEncodedContent ToFormUrl(this EncryptedAuthorizationRespons
{ "response", response.ToString() }
};

response.State.IfSome(state => content["state"] = state);

return new FormUrlEncodedContent(content);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using LanguageExt;
using WalletFramework.Core.Functional;
using WalletFramework.Oid4Vc.Oid4Vp.AuthResponse.Encryption.Abstractions;
using WalletFramework.Oid4Vc.Oid4Vp.Models;

Expand All @@ -14,10 +15,15 @@ public async Task<EncryptedAuthorizationResponse> Encrypt(
{
var verifierPubKey = await verifierKeyService.GetPublicKey(request);

var supportedAlgorithms = (request.ClientMetadata?.EncryptedResponseEncValuesSupported).AsOption().Match(
encValues => encValues,
() => (request.ClientMetadata?.AuthorizationEncryptedResponseEnc).AsOption().OnSome(encValue => new[] { encValue })
);

return response.Encrypt(
verifierPubKey,
request.Nonce,
request.ClientMetadata?.AuthorizationEncryptedResponseEnc,
supportedAlgorithms,
mdocNonce);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace WalletFramework.Oid4Vc.Oid4Vp.Errors;

public record VpFormatsNotSupportedError : VpError
{
private const string Code = "vp_formats_not_supported";

public VpFormatsNotSupportedError(string message) : base(Code, message)
{
}
};
24 changes: 21 additions & 3 deletions src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public record ClientMetadata
// Needed for Newtonsoft Json Serialization
public ClientMetadata(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add a unit test for parsing ClientMetadata

string? authorizationEncryptedResponseEnc,
string[]? encryptedResponseEncValuesSupported,
string[] redirectUris,
string? clientName,
string? clientUri,
Expand All @@ -21,9 +22,11 @@ public ClientMetadata(
string? jwksUri,
string? policyUri,
string? tosUri,
Formats formats)
Formats vpFormats,
Formats vpFormatsSupported)
{
AuthorizationEncryptedResponseEnc = authorizationEncryptedResponseEnc;
EncryptedResponseEncValuesSupported = encryptedResponseEncValuesSupported;
RedirectUris = redirectUris;
ClientName = clientName;
ClientUri = clientUri;
Expand All @@ -33,15 +36,23 @@ public ClientMetadata(
JwksUri = jwksUri;
PolicyUri = policyUri;
TosUri = tosUri;
Formats = formats;
VpFormats = vpFormats;
VpFormatsSupported = vpFormatsSupported;
}

/// <summary>
/// Defined the encoding that should be used when an encrypted Auth Response is requested by the verifier.
/// Replaced by encrypted_response_enc_values_supported but kept for now for backwards compatibility.
/// </summary>
[JsonProperty("authorization_encrypted_response_enc")]
public string? AuthorizationEncryptedResponseEnc { get; init; }

/// <summary>
/// Defined the encoding that should be used when an encrypted Auth Response is requested by the verifier.
/// </summary>
[JsonProperty("encrypted_response_enc_values_supported")]
public string[]? EncryptedResponseEncValuesSupported { get; init; }

/// <summary>
/// The redirect URIs of the client (verifier).
/// </summary>
Expand Down Expand Up @@ -94,7 +105,14 @@ public ClientMetadata(

/// <summary>
/// The URI to a human-readable terms of service document for the client (verifier).
/// This is deprecated and replaced by vp_formats_supported but kept for now for backwards compatibility.
/// </summary>
[JsonProperty("vp_formats")]
public Formats Formats { get; init; }
public Formats VpFormats { get; init; }

/// <summary>
/// The URI to a human-readable terms of service document for the client (verifier).
/// </summary>
[JsonProperty("vp_formats_supported")]
public Formats VpFormatsSupported { get; init; }
}
8 changes: 4 additions & 4 deletions src/WalletFramework.Oid4Vc/Oid4Vp/Models/MDocFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ public class MDocFormat
/// <summary>
/// Gets the names of supported algorithms.
/// </summary>
[JsonProperty("alg")]
public string[]? Alg { get; private set; }
[JsonProperty("issuerauth_alg_values")]
public string[]? IssuerAuthAlgValues { get; init; }

/// <summary>
/// Gets the names of supported proof types.
/// </summary>
[JsonProperty("proof_type")]
public string[]? ProofTypes { get; private set; }
[JsonProperty("deviceauth_alg_values")]
public string[]? DeviceAuthAlgValues { get; init; }
}
57 changes: 57 additions & 0 deletions src/WalletFramework.Oid4Vc/Oid4Vp/Models/WalletMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Newtonsoft.Json.Linq;

namespace WalletFramework.Oid4Vc.Oid4Vp.Models;

public record WalletMetadata
{
private const string VpFormatsSupportedIdentifier = "vp_formats_supported";
private const string ClientIdPrefixesSupportedIdentifier = "client_id_prefixes_supported";
//TODO: Remove the following identifier in the future, it is deprecated but kept for backwards compatibility for now.
private const string ClientIdSchemesSupportedIdentifier = "client_id_schemes_supported";

public Formats VpFormatsSupported { get; }

public ClientIdScheme[] ClientIdPrefixesSupported { get; }

private WalletMetadata(Formats vpFormatsSupported, ClientIdScheme[] clientIdPrefixesSupported)
{
VpFormatsSupported = vpFormatsSupported;
ClientIdPrefixesSupported = clientIdPrefixesSupported;
}

public static WalletMetadata CreateDefault()
{
var vpFormatsSupported = new Formats
{
SdJwtVcFormat = new SdJwtFormat
{
IssuerSignedJwtAlgValues = ["ES256", "ES384", "ES512", "RS256"],
KeyBindingJwtAlgValues = ["ES256"]
},
SdJwtDcFormat = new SdJwtFormat
{
IssuerSignedJwtAlgValues = ["ES256", "ES384", "ES512", "RS256"],
KeyBindingJwtAlgValues = ["ES256"]
},
MDocFormat = new MDocFormat
{
IssuerAuthAlgValues = ["-7", "-35", "-36", "-8"],
DeviceAuthAlgValues = ["-7"]
}
};

var clientIdPrefixesSupported = new []{(ClientIdScheme)ClientIdScheme.RedirectUriScheme, (ClientIdScheme)ClientIdScheme.X509SanDnsScheme};

return new WalletMetadata(vpFormatsSupported, clientIdPrefixesSupported);
}

public string ToJsonString()
{
return new JObject
{
[VpFormatsSupportedIdentifier] = JObject.FromObject(VpFormatsSupported),
[ClientIdPrefixesSupportedIdentifier] = new JArray {ClientIdPrefixesSupported.Select(x => x.AsString())},
[ClientIdSchemesSupportedIdentifier] = new JArray {ClientIdPrefixesSupported.Select(x => x.AsString())},
}.ToString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,6 @@ private async Task<Option<IEnumerable<ICredential>>> GetMatchingCredentials(
{
return record.DocType == inputDescriptor.Id
&& record.Mdoc.IssuerSigned.IssuerAuth.ProtectedHeaders.Value.TryGetValue(new CoseLabel(1), out var alg)
&& supportedFormatSigningAlgorithms.Match(
formats => formats.MDocFormat?.Alg?.Contains(alg.ToString()) ?? true,
() => inputDescriptor.Formats?.MDocFormat?.Alg?.Contains(alg.ToString()) ?? true)
&& inputDescriptor.Constraints.Fields!.All(field =>
{
try
Expand Down
Loading
Loading