Skip to content

Commit

Permalink
Implement OID4VP for mdocs (#157)
Browse files Browse the repository at this point in the history
* adjust OID4VP signatures

Signed-off-by: Kevin <[email protected]>

* adjust ICredential

Signed-off-by: Kevin <[email protected]>

* implement base64url type

Signed-off-by: Kevin <[email protected]>

* refactorings

Signed-off-by: Kevin <[email protected]>

* adjust mdoc lib folder structure

Signed-off-by: Kevin <[email protected]>

* rename namespaces to issuer namespaces

Signed-off-by: Kevin <[email protected]>

* security stuff in core project

Signed-off-by: Kevin <[email protected]>

* add keyId to MdocRecord

Signed-off-by: Kevin <[email protected]>

* add comments to ClientMetadata.cs

Signed-off-by: Kevin <[email protected]>

* implement mdoc presentation

Signed-off-by: Kevin <[email protected]>

* implement mdoc oid4vp

Signed-off-by: Kevin <[email protected]>

* adjust tests

Signed-off-by: Kevin <[email protected]>

* some cleanup

Signed-off-by: Kevin <[email protected]>

* bump dotnet version in pipeline

Signed-off-by: Kevin <[email protected]>

* bump nuget version in pipeline

Signed-off-by: Kevin <[email protected]>

* bump nuget version in pipeline

Signed-off-by: Kevin <[email protected]>

* bump nuget version in pipeline

Signed-off-by: Kevin <[email protected]>

* minor refactor

Signed-off-by: Kevin <[email protected]>

* fix merge

Signed-off-by: Kevin <[email protected]>

* rename SdJwtSignerService to SdJwtSigner

Signed-off-by: Kevin <[email protected]>

* introduce raw signature

Signed-off-by: Kevin <[email protected]>

* fix merge

Signed-off-by: Kevin <[email protected]>

* adjust cose signature

Signed-off-by: Kevin <[email protected]>

* add string funcs

Signed-off-by: Kevin <[email protected]>

---------

Signed-off-by: Kevin <[email protected]>
  • Loading branch information
Dindexx authored Aug 19, 2024
1 parent 84031a1 commit 8016504
Show file tree
Hide file tree
Showing 122 changed files with 2,511 additions and 1,418 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/publish-nuget.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ jobs:
echo "APP_VERSION=$VERSION$SUFFIX" >> $GITHUB_ENV
- name: Setup NuGet
uses: NuGet/[email protected]
uses: NuGet/setup-nuget@v2
with:
nuget-version: 6.10.2

- name: Restore dependencies
run: nuget restore $SOLUTION

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 3.1.*
dotnet-version: 8.0.*

# - name: Install libindy library
# run: |
Expand Down

This file was deleted.

23 changes: 23 additions & 0 deletions src/WalletFramework.Core/Base64Url/Base64UrlString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.IdentityModel.Tokens;

namespace WalletFramework.Core.Base64Url;

public readonly struct Base64UrlString
{
private string Value { get; }

private Base64UrlString(string value)
{
Value = value;
}

public override string ToString() => Value;

public static implicit operator string(Base64UrlString base64UrlString) => base64UrlString.ToString();

public static Base64UrlString CreateBase64UrlString(IEnumerable<byte> base64UrlBytes)
{
var result = Base64UrlEncoder.Encode(base64UrlBytes.ToArray());
return new Base64UrlString(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace WalletFramework.Core.Credentials.Abstractions;

/// <summary>
/// This interface is used to represent a credential.
/// </summary>
public interface ICredential
{
CredentialId GetId();
}
13 changes: 7 additions & 6 deletions src/WalletFramework.Core/Cryptography/Abstractions/IKeyStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@ public interface IKeyStore
Task<KeyId> GenerateKey(string alg = "ES256", bool isPermanent = true);

/// <summary>
/// Asynchronously loads a key by its identifier and returns it as a JSON Web Key (JWK) containing the public key
/// information.
/// Gets the public key of the pair
/// </summary>
/// <param name="keyId">The identifier of the key to load.</param>
/// <returns>A <see cref="Task{TResult}" /> representing the loaded key as a JWK string.</returns>
Task<string> LoadKey(KeyId keyId);
/// <param name="keyId">The identifier of the key pair.</param>
/// <returns>
/// The public key
/// </returns>
Task<PublicKey> GetPublicKey(KeyId keyId);

/// <summary>
/// Asynchronously signs the given payload using the key identified by the provided key ID.
/// </summary>
/// <param name="keyId">The identifier of the key to use for signing.</param>
/// <param name="payload">The payload to sign.</param>
/// <returns>A <see cref="Task{TResult}" /> representing the signed payload as a byte array.</returns>
Task<byte[]> Sign(KeyId keyId, byte[] payload);
Task<RawSignature> Sign(KeyId keyId, byte[] payload);

/// <summary>
/// Asynchronously deletes the key associated with the provided key ID.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using WalletFramework.Core.Functional;

namespace WalletFramework.Core.Cryptography.Errors;

public record InvalidSignatureError(string Message, Exception E) : Error(Message, E);
21 changes: 21 additions & 0 deletions src/WalletFramework.Core/Cryptography/Models/PublicKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using WalletFramework.Core.Base64Url;

namespace WalletFramework.Core.Cryptography.Models;

public record PublicKey(Base64UrlString X, Base64UrlString Y)
{
public string KeyType => "EC";

public string Curve => "P-256";
}

public static class PublicKeyFun
{
public static object ToJwkObj(this PublicKey publicKey) => new
{
kty = publicKey.KeyType,
crv = publicKey.Curve,
x = publicKey.X.ToString(),
y = publicKey.Y.ToString()
};
}
53 changes: 53 additions & 0 deletions src/WalletFramework.Core/Cryptography/Models/RawSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Org.BouncyCastle.Asn1;
using WalletFramework.Core.Cryptography.Errors;
using WalletFramework.Core.Functional;

namespace WalletFramework.Core.Cryptography.Models;

public readonly struct RawSignature
{
private byte[] Value { get; }

public byte[] AsByteArray => Value;

public RawSignature(byte[] value)
{
Value = value;
}

public static implicit operator byte[](RawSignature signature) => signature.AsByteArray;

public static Validation<RawSignature> FromDerSignature(byte[] derSignature)
{
try
{
var seq = (Asn1Sequence)Asn1Object.FromByteArray(derSignature);
var r = ((DerInteger)seq[0]).Value;
var s = ((DerInteger)seq[1]).Value;
var rBytes = r.ToByteArrayUnsigned();
var sBytes = s.ToByteArrayUnsigned();
rBytes = PadTo32Bytes(rBytes);
sBytes = PadTo32Bytes(sBytes);

var signatureBytes = rBytes.Concat(sBytes).ToArray();
return new RawSignature(signatureBytes);
}
catch (Exception e)
{
return new InvalidSignatureError("The signature could not be transformed to RAW format", e);
}
}

private static byte[] PadTo32Bytes(byte[] value)
{
if (value.Length == 32)
return value;

if (value.Length > 32)
throw new ArgumentException("Value is too large to fit in 32 bytes");

var padded = new byte[32];
Array.Copy(value, 0, padded, 32 - value.Length, value.Length);
return padded;
}
}
27 changes: 27 additions & 0 deletions src/WalletFramework.Core/Encoding/Sha256Hash.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Security.Cryptography;

namespace WalletFramework.Core.Encoding;

public readonly struct Sha256Hash
{
private byte[] Value { get; }

private Sha256Hash(byte[] value)
{
Value = value;
}

public byte[] AsBytes => Value;

public override string ToString() => Value.ToString();

public static implicit operator byte[](Sha256Hash sha256Hash) => sha256Hash.Value;

public static Sha256Hash ComputeHash(byte[] value)
{
var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(value);
return new Sha256Hash(hash);
}
}

9 changes: 8 additions & 1 deletion src/WalletFramework.Core/Functional/OptionFun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ public static Option<IEnumerable<TR>> TraverseAny<T, TR>(
return list.Any() ? list : Option<IEnumerable<TR>>.None;
});
}


public static Option<IEnumerable<T>> AsOption<T>(this IEnumerable<T> enumerable) =>
// ReSharper disable once PossibleMultipleEnumeration
enumerable.IsEmpty()
? Option<IEnumerable<T>>.None
// ReSharper disable once PossibleMultipleEnumeration
: Some(enumerable);

public static T UnwrapOrThrow<T>(this Option<T> option, Exception e) =>
option.Match(
t => t,
Expand Down
10 changes: 10 additions & 0 deletions src/WalletFramework.Core/Functional/Validation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public Validation(Validation<Error, T> value)

public static class ValidationFun
{
public static Validation<Func<T2, Func<T3, Func<T4, Func<T5, Func<T6, Func<T7, TR>>>>>>> Apply<T1, T2, T3, T4, T5, T6, T7, TR>(
this Validation<Func<T1, T2, T3, T4, T5, T6, T7, TR>> valF,
Validation<T1> valT) =>
Apply(valF.Select(Prelude.curry), valT);

public static Validation<Func<T2, Func<T3, Func<T4, Func<T5, Func<T6, TR>>>>>> Apply<T1, T2, T3, T4, T5, T6, TR>(
this Validation<Func<T1, T2, T3, T4, T5, T6, TR>> valF,
Validation<T1> valT) =>
Expand Down Expand Up @@ -211,6 +216,11 @@ from t1 in validation
let t2 = onSucc(t1)
select t2;

public static Validation<Unit> OnSuccess<T1>(this Validation<T1> validation, Func<T1, Validation<Unit>> onSucc) =>
from t1 in validation
from r in onSucc(t1)
select r;

public static Task<Validation<Unit>> OnSuccess<T1>(this Validation<T1> validation, Func<T1, Task> onSucc)
{
var adapter = new Func<T1, Task<Unit>>(async arg =>
Expand Down
6 changes: 6 additions & 0 deletions src/WalletFramework.Core/String/StringFun.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace WalletFramework.Core.String;

public static class StringFun
{
public static bool IsNullOrEmpty(this string? value) => string.IsNullOrEmpty(value);
}
12 changes: 12 additions & 0 deletions src/WalletFramework.Core/Versioning/VersionFun.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace WalletFramework.Core.Versioning;

public static class VersionFun
{
public static string ToMajorMinorString(this Version version)
{
var major = version.Major.ToString();
var minor = version.Minor.ToString();

return major + "." + minor;
}
}
2 changes: 2 additions & 0 deletions src/WalletFramework.Core/WalletFramework.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
</ItemGroup>
</Project>
53 changes: 53 additions & 0 deletions src/WalletFramework.MdocLib/Cbor/CborByteString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using PeterO.Cbor;
using WalletFramework.Core.Functional;

namespace WalletFramework.MdocLib.Cbor;

/// <summary>
/// A CBOR encoded byte string which can be tagged or untagged
/// </summary>
public readonly struct CborByteString
{
private CBORObject Value { get; }

private CborByteString(CBORObject value) => Value = value;

public CBORObject Decode() => CBORObject.DecodeFromBytes(Value.GetByteString());

public CBORObject AsCbor => Value;

public static implicit operator CBORObject(CborByteString cborByteString) => cborByteString.Value;

public static Validation<CborByteString> ValidCborByteString(CBORObject cbor)
{
try
{
var bs = cbor.GetByteString();
CBORObject.DecodeFromBytes(bs);
return new CborByteString(cbor);
}
catch (Exception e)
{
return new InvalidCborByteStringError(cbor.ToString(), e);
}
}
}

public static class CborByteStringFun
{
public static CborByteString ToCborByteString(this CBORObject cbor)
{
var encodedByteString = CBORObject.FromObject(cbor.EncodeToBytes());
return CborByteString
.ValidCborByteString(encodedByteString)
.UnwrapOrThrow(new InvalidOperationException("CborByteString implementation is corrupt"));
}

public static CborByteString ToTaggedCborByteString(this CBORObject cbor)
{
var wrappedByteString = CBORObject.FromObjectAndTag(cbor.EncodeToBytes(), 24);
return CborByteString
.ValidCborByteString(wrappedByteString)
.UnwrapOrThrow(new InvalidOperationException("CborByteString implementation is corrupt"));
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using PeterO.Cbor;
using WalletFramework.Core.Functional;
using WalletFramework.MdocLib.Common;

namespace WalletFramework.MdocLib;
namespace WalletFramework.MdocLib.Cbor;

internal static class CborFun
{
public static bool IsNull(this CBORObject? cborObject) => cborObject is null || cborObject.IsNull;

public static Validation<CBORObject> GetByLabel(this CBORObject cborObject, string label)
public static Validation<CBORObject> GetByLabel(this CBORObject cborObject, CBORObject label)
{
CBORObject value;
try
Expand All @@ -17,17 +16,32 @@ public static Validation<CBORObject> GetByLabel(this CBORObject cborObject, stri
}
catch (Exception e)
{
return new CborIsNotAMapOrAnArrayError(cborObject.ToString(), label, e);
return new CborIsNotAMapOrAnArrayError(cborObject.ToString(), label.ToString(), e);
}

if (value.IsNull())
{
return new CborFieldNotFoundError(label);
return new CborFieldNotFoundError(label.ToString());
}

return value;
}

public static Validation<CBORObject> GetByLabel(this CBORObject cborObject, string label)
{
CBORObject value;
try
{
value = CBORObject.FromObject(label);
}
catch (Exception e)
{
return new CborIsNotATextStringError(label, e);
}

return cborObject.GetByLabel(value);
}

public static Validation<CBORObject> GetByIndex(this CBORObject cbor, uint index)
{
CBORObject value;
Expand Down
Loading

0 comments on commit 8016504

Please sign in to comment.