diff --git a/src/WalletFramework.Core/Base64Url/Base64UrlString.cs b/src/WalletFramework.Core/Base64Url/Base64UrlString.cs
index 1d288cdb..ea6669e5 100644
--- a/src/WalletFramework.Core/Base64Url/Base64UrlString.cs
+++ b/src/WalletFramework.Core/Base64Url/Base64UrlString.cs
@@ -5,9 +5,11 @@ namespace WalletFramework.Core.Base64Url;
public readonly struct Base64UrlString
{
private string Value { get; }
-
+
public byte[] AsByteArray => Base64UrlEncoder.DecodeBytes(Value);
+ public string AsString => Value;
+
private Base64UrlString(string value)
{
Value = value;
diff --git a/src/WalletFramework.Core/WalletFramework.Core.csproj b/src/WalletFramework.Core/WalletFramework.Core.csproj
index 3b7367b5..a9bb1519 100644
--- a/src/WalletFramework.Core/WalletFramework.Core.csproj
+++ b/src/WalletFramework.Core/WalletFramework.Core.csproj
@@ -5,6 +5,7 @@
enable
+
diff --git a/src/WalletFramework.MdocLib/Mdoc.cs b/src/WalletFramework.MdocLib/Mdoc.cs
index 5b8e0f19..f0d55b64 100644
--- a/src/WalletFramework.MdocLib/Mdoc.cs
+++ b/src/WalletFramework.MdocLib/Mdoc.cs
@@ -104,11 +104,10 @@ public static Validation FromIssuerSigned(string base64UrlencodedCborByteS
});
var validateIntegrity = new List>
- {
- MdocFun.DocTypeMatches,
- MdocFun.DigestsMatch
- }
- .AggregateValidators();
+ {
+ MdocFun.DocTypeMatches,
+ MdocFun.DigestsMatch
+ }.AggregateValidators();
return
from bytes in decodeBase64Url(base64UrlencodedCborByteString)
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/Oid4VciClientService.cs b/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/Oid4VciClientService.cs
index a86ed70c..a2c63da0 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/Oid4VciClientService.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/Oid4VciClientService.cs
@@ -136,8 +136,7 @@ from issState in code.IssuerState
null,
null);
- var authServerMetadata =
- await FetchAuthorizationServerMetadataAsync(issuerMetadata);
+ var authServerMetadata = await FetchAuthorizationServerMetadataAsync(issuerMetadata);
_httpClient.DefaultRequestHeaders.Clear();
var response = await _httpClient.PostAsync(
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs
new file mode 100644
index 00000000..1553e6bc
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs
@@ -0,0 +1,50 @@
+using Hyperledger.Aries.Extensions;
+using Jose;
+using LanguageExt;
+using WalletFramework.Oid4Vc.Oid4Vp.Jwk;
+using WalletFramework.Oid4Vc.Oid4Vp.Models;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.AuthResponse.Encryption;
+
+public record EncryptedAuthorizationResponse(string Jwe)
+{
+ public override string ToString() => Jwe;
+}
+
+public static class EncryptedAuthorizationResponseFun
+{
+ public static FormUrlEncodedContent ToFormUrl(this EncryptedAuthorizationResponse response)
+ {
+ var content = new Dictionary
+ {
+ { "response", response.ToString() },
+ };
+
+ return new FormUrlEncodedContent(content);
+ }
+
+ public static EncryptedAuthorizationResponse Encrypt(
+ this AuthorizationResponse response,
+ AuthorizationRequest authorizationRequest,
+ Option mdocNonce)
+ {
+ var verifierPubKey = authorizationRequest.ClientMetadata!.Jwks.First();
+
+ var headers = new Dictionary
+ {
+ { "apv", authorizationRequest.Nonce },
+ { "kid", verifierPubKey.Kid }
+ };
+
+ mdocNonce.IfSome(nonce => headers.Add("apu", nonce.AsBase64Url.ToString()));
+
+ var jwe = JWE.EncryptBytes(
+ response.ToJson().GetUTF8Bytes(),
+ new [] { new JweRecipient(JweAlgorithm.ECDH_ES, verifierPubKey.ToEcdh()) },
+ JweEncryption.A256GCM,
+ mode: SerializationMode.Compact,
+ extraProtectedHeaders: headers);
+
+ return new EncryptedAuthorizationResponse(jwe);
+ }
+}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Jwk/JwkFun.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Jwk/JwkFun.cs
new file mode 100644
index 00000000..b2c841bc
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Jwk/JwkFun.cs
@@ -0,0 +1,23 @@
+using System.Security.Cryptography;
+using Microsoft.IdentityModel.Tokens;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.Jwk;
+
+public static class JwkFun
+{
+ public static ECDiffieHellman ToEcdh(this JsonWebKey jwk)
+ {
+ var ecParameters = new ECParameters
+ {
+ Q =
+ {
+ X = Base64UrlEncoder.DecodeBytes(jwk.X),
+ Y = Base64UrlEncoder.DecodeBytes(jwk.Y)
+ },
+ // TODO: Map curve from jwk
+ Curve = ECCurve.NamedCurves.nistP256
+ };
+
+ return ECDiffieHellman.Create(ecParameters);
+ }
+}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs
index af71fb43..c6f59643 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs
@@ -11,8 +11,8 @@ namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
///
public record AuthorizationRequest
{
- private const string DirectPost = "direct_post";
- private const string DirectPostJwt = "direct_post.jwt";
+ public const string DirectPost = "direct_post";
+ public const string DirectPostJwt = "direct_post.jwt";
private const string VpToken = "vp_token";
@@ -45,6 +45,9 @@ public record AuthorizationRequest
///
[JsonProperty("response_uri")]
public string ResponseUri { get; }
+
+ [JsonProperty("response_mode")]
+ public string ResponseMode { get; }
///
/// Gets the client metadata. Contains the Verifier metadata.
@@ -89,6 +92,7 @@ private AuthorizationRequest(
string clientId,
string nonce,
string responseUri,
+ string responseMode,
ClientMetadata? clientMetadata,
string? clientMetadataUri,
string? scope,
@@ -101,6 +105,7 @@ private AuthorizationRequest(
Nonce = nonce;
PresentationDefinition = presentationDefinition;
ResponseUri = responseUri;
+ ResponseMode = responseMode;
Scope = scope;
State = state;
}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationResponse.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationResponse.cs
index 901ac78a..039f1f11 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationResponse.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationResponse.cs
@@ -1,4 +1,5 @@
using Newtonsoft.Json;
+using WalletFramework.Oid4Vc.Oid4Vp.PresentationExchange.Models;
namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
@@ -10,18 +11,30 @@ public class AuthorizationResponse
///
/// Gets or sets the VP Token.
///
- [JsonProperty ("vp_token")]
+ [JsonProperty("vp_token")]
public string VpToken { get; set; } = null!;
///
/// Gets or sets the Presentation Submission.
///
- [JsonProperty ("presentation_submission")]
- public string PresentationSubmission { get; set; } = null!;
+ [JsonProperty("presentation_submission")]
+ public PresentationSubmission PresentationSubmission { get; set; } = null!;
///
/// Gets or sets the State.
///
- [JsonProperty ("state", NullValueHandling = NullValueHandling.Ignore)]
- public string? State { get; set; } = null!;
+ [JsonProperty("state", NullValueHandling = NullValueHandling.Ignore)]
+ public string? State { get; set; }
+}
+
+public static class AuthorizationResponseFun
+{
+ public static FormUrlEncodedContent ToFormUrl(this AuthorizationResponse authorizationResponse)
+ {
+ var json = JsonConvert.SerializeObject(authorizationResponse);
+ var nameValueCollection =
+ JsonConvert.DeserializeObject>(json)!.ToList();
+
+ return new FormUrlEncodedContent(nameValueCollection);
+ }
}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientJwksConverter.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientJwksConverter.cs
new file mode 100644
index 00000000..f716e6f5
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientJwksConverter.cs
@@ -0,0 +1,40 @@
+using Microsoft.IdentityModel.Tokens;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using WalletFramework.Core.Functional;
+using WalletFramework.Core.Json;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
+
+public class ClientJwksConverter : JsonConverter>
+{
+ public override bool CanRead => true;
+
+ public override bool CanWrite => false;
+
+ public override void WriteJson(JsonWriter writer, List? value, JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override List ReadJson(
+ JsonReader reader,
+ Type objectType,
+ List? existingValue,
+ bool hasExistingValue,
+ JsonSerializer serializer)
+ {
+ var json = JObject.Load(reader);
+ var validKeysArray =
+ from keys in json.GetByKey("keys")
+ from keysArray in keys.ToJArray()
+ select keysArray;
+
+ var result = validKeysArray.OnSuccess(keysArray =>
+ {
+ return keysArray.Select(keyToken => JsonWebKey.Create(keyToken.ToString())).ToList();
+ }).UnwrapOrThrow();
+
+ return result;
+ }
+}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientMetadata.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientMetadata.cs
index 635972a2..828fd3e9 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientMetadata.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/ClientMetadata.cs
@@ -1,3 +1,4 @@
+using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
@@ -5,7 +6,6 @@ namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
///
/// Represents the metadata of a client (verifier).
///
-// TODO: Rename this to VerifierMetadata
public record ClientMetadata
{
///
@@ -38,8 +38,9 @@ public record ClientMetadata
[JsonProperty("logo_uri")]
public string? LogoUri { get; }
- // TODO: JWKs of the verifier
- // public List PublicKeys { get; }
+ [JsonProperty("jwks")]
+ [JsonConverter(typeof(ClientJwksConverter))]
+ public List Jwks { get; }
///
/// The URI to a human-readable privacy policy document for the client (verifier).
@@ -54,7 +55,15 @@ public record ClientMetadata
public string? TosUri { get; }
[JsonConstructor]
- private ClientMetadata(string? clientName, string? clientUri, string[]? contacts, string? logoUri, string? policyUri, string? tosUri, string[] redirectUris)
+ private ClientMetadata(
+ string? clientName,
+ string? clientUri,
+ string[]? contacts,
+ string? logoUri,
+ string? policyUri,
+ string? tosUri,
+ string[] redirectUris,
+ List jwks)
{
ClientName = clientName;
ClientUri = clientUri;
@@ -63,5 +72,6 @@ private ClientMetadata(string? clientName, string? clientUri, string[]? contacts
PolicyUri = policyUri;
TosUri = tosUri;
RedirectUris = redirectUris;
+ Jwks = jwks;
}
}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs
index 36968233..aab8247c 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs
@@ -12,6 +12,7 @@
using WalletFramework.MdocLib.Security;
using WalletFramework.MdocVc;
using WalletFramework.Oid4Vc.Oid4Vci.CredConfiguration.Models;
+using WalletFramework.Oid4Vc.Oid4Vp.AuthResponse.Encryption;
using WalletFramework.Oid4Vc.Oid4Vp.Models;
using WalletFramework.Oid4Vc.Oid4Vp.PresentationExchange.Services;
using WalletFramework.SdJwtVc.Models.Records;
@@ -126,6 +127,7 @@ from path in field.Path.Select(path => path.TrimStart('$', '.'))
var toDisclose = claims.Select(claim =>
{
+ // TODO: This is needed because in mdoc the requested attributes look like this: $[Namespace][ElementId]. Refactor this more clean
var keys = claim.Split(new[] { "[", "]" }, StringSplitOptions.RemoveEmptyEntries);
var nameSpace = NameSpace.ValidNameSpace(keys[0]).UnwrapOrThrow();
@@ -170,22 +172,12 @@ from path in field.Path.Select(path => path.TrimStart('$', '.'))
presentationMaps.Select(tuple => (tuple.InputDescriptorId, tuple.Presentation, tuple.Format)).ToArray()
);
- var httpClient = _httpClientFactory.CreateClient();
- httpClient.DefaultRequestHeaders.Clear();
- if (clientAttestation is not null)
- httpClient.AddClientAttestationPopHeader(clientAttestation);
-
- var json = SerializeObject(authorizationResponse);
- var nameValueCollection = DeserializeObject>(json)!.ToList();
-
- // TODO: This is only a hack until the encryption response is implemented
- mdocNonce.IfSome(nonce =>
+ var content = authorizationRequest.ResponseMode switch
{
- var pair = new KeyValuePair("apu", nonce.AsBase64Url.ToString());
- nameValueCollection.Add(pair);
- });
-
- var content = new FormUrlEncodedContent(nameValueCollection);
+ AuthorizationRequest.DirectPost => authorizationResponse.ToFormUrl(),
+ AuthorizationRequest.DirectPostJwt => authorizationResponse.Encrypt(authorizationRequest, mdocNonce).ToFormUrl(),
+ _ => throw new ArgumentOutOfRangeException(nameof(authorizationRequest.ResponseMode))
+ };
var message = new HttpRequestMessage
{
@@ -193,13 +185,15 @@ from path in field.Path.Select(path => path.TrimStart('$', '.'))
Method = HttpMethod.Post,
Content = content
};
-
+
// TODO: Introduce timeout
+ var httpClient = _httpClientFactory.CreateClient();
+ httpClient.DefaultRequestHeaders.Clear();
var responseMessage = await httpClient.SendAsync(message);
if (!responseMessage.IsSuccessStatusCode)
{
var str = await responseMessage.Content.ReadAsStringAsync();
- throw new InvalidOperationException($"Authorization Response could not be sent with message {str}");
+ throw new InvalidOperationException($"Authorization Response failed with message {str}");
}
var presentedCredentials = presentationMaps.Select(presentationMap =>
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpHaipClient.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpHaipClient.cs
index f61baf6e..0d47eff1 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpHaipClient.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpHaipClient.cs
@@ -56,7 +56,7 @@ public async Task CreateAuthorizationResponseAsync(
return new AuthorizationResponse
{
- PresentationSubmission = SerializeObject(presentationSubmission),
+ PresentationSubmission = presentationSubmission,
VpToken = vpToken.Count > 1 ? SerializeObject(vpToken) : vpToken[0],
State = authorizationRequest.State
};
diff --git a/src/WalletFramework.Oid4Vc/WalletFramework.Oid4Vc.csproj b/src/WalletFramework.Oid4Vc/WalletFramework.Oid4Vc.csproj
index b83cad8b..801ff212 100644
--- a/src/WalletFramework.Oid4Vc/WalletFramework.Oid4Vc.csproj
+++ b/src/WalletFramework.Oid4Vc/WalletFramework.Oid4Vc.csproj
@@ -1,5 +1,4 @@
-
netstandard2.1
enable