Skip to content

Commit

Permalink
Expanded and tested ExtendedJsonWebKeyConverter, cleaned up formattin…
Browse files Browse the repository at this point in the history
…g, and removed unsupported EdDSA factory method.
  • Loading branch information
scottbrady91 committed Mar 30, 2024
1 parent 960e5c5 commit f38a273
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using ScottBrady.IdentityModel.Crypto;
using ScottBrady.IdentityModel.Tokens;

namespace ScottBrady.IdentityModel.Samples.AspNetCore;

public class SampleOptions
{
public readonly EdDsaSecurityKey EdDsaPublicKey = new EdDsaSecurityKey(
EdDsa.Create(new EdDsaParameters(ExtendedSecurityAlgorithms.Curves.Ed25519) {X =Convert.FromBase64String("doaS7QILHBdnPULlgs1fX0MWpd1wak14r1yT6ae/b4M=")}));

public readonly EdDsaSecurityKey EdDsaPrivateKey= new EdDsaSecurityKey(
EdDsa.Create(new EdDsaParameters(ExtendedSecurityAlgorithms.Curves.Ed25519) {D =Convert.FromBase64String("TYXei5+8Qd2ZqKIlEuJJ3S50WYuocFTrqK+3/gHVH9B2hpLtAgscF2c9QuWCzV9fQxal3XBqTXivXJPpp79vgw==")}));
private static readonly EdDsa _key = EdDsa.Create(ExtendedSecurityAlgorithms.Curves.Ed25519);

public readonly EdDsaSecurityKey EdDsaPublicKey = new EdDsaSecurityKey(_key);
public readonly EdDsaSecurityKey EdDsaPrivateKey= new EdDsaSecurityKey(_key);
}
4 changes: 0 additions & 4 deletions src/ScottBrady.IdentityModel.AspNetCore/Assembly.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@
<None Include="..\..\icon.png" Pack="true" PackagePath="icon.png" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="ScottBrady.IdentityModel.Tests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>

</Project>
63 changes: 32 additions & 31 deletions src/ScottBrady.IdentityModel/Encoding/Base16.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,47 @@ namespace ScottBrady.IdentityModel;
/// </summary>
public static class Base16
{
private static readonly string[] LookupTable =
{
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff"
};
private static readonly string[] _lookupTable =
{
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff"
};

public static string Encode(byte[] value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (value == null) throw new ArgumentNullException(nameof(value));

var stringBuilder = new StringBuilder();
foreach (var b in value) stringBuilder.Append(LookupTable[b]);
var stringBuilder = new StringBuilder();
foreach (var b in value) stringBuilder.Append(_lookupTable[b]);

return stringBuilder.ToString();
}
return stringBuilder.ToString();
}

public static byte[] Decode(string value)
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value));
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value));

var bytes = new byte[value.Length / 2];
var bytes = new byte[value.Length / 2];

for (var i = 0; i < value.Length; i += 2) {
bytes[i / 2] = Convert.ToByte(value.Substring(i, 2), 16);
}

return bytes;
for (var i = 0; i < value.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(value.Substring(i, 2), 16);
}

return bytes;
}
}
74 changes: 37 additions & 37 deletions src/ScottBrady.IdentityModel/Encoding/Base62.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,55 @@ namespace ScottBrady.IdentityModel;
public static class Base62
{
public const string CharacterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

public static string Encode(byte[] value)
{
var convertedBytes = BaseConvert(value, 256, 62);

var builder = new StringBuilder();
foreach (var b in convertedBytes)
{
builder.Append(CharacterSet[b]);
}

return builder.ToString();
var convertedBytes = BaseConvert(value, 256, 62);

var builder = new StringBuilder();
foreach (var b in convertedBytes)
{
builder.Append(CharacterSet[b]);
}

return builder.ToString();
}

public static byte[] Decode(string value)
{
var arr = new byte[value.Length];
for (var i = 0; i < arr.Length; i++)
{
arr[i] = (byte)CharacterSet.IndexOf(value[i]);
}

return BaseConvert(arr, 62, 256);
var arr = new byte[value.Length];
for (var i = 0; i < arr.Length; i++)
{
arr[i] = (byte)CharacterSet.IndexOf(value[i]);
}


return BaseConvert(arr, 62, 256);
}

private static byte[] BaseConvert(byte[] source, int sourceBase, int targetBase)
{
if (source == null) throw new ArgumentNullException(nameof(source));

int count;
var result = new List<int>();

while ((count = source.Length) > 0)
if (source == null) throw new ArgumentNullException(nameof(source));

int count;
var result = new List<int>();

while ((count = source.Length) > 0)
{
var remainder = 0;
var quotients = new List<byte>();

for (var i = 0; i != count; i++)
{
var remainder = 0;
var quotients = new List<byte>();

for (var i = 0; i != count; i++)
{
var accumulator = source[i] + remainder * sourceBase;
var quotient = Math.DivRem(accumulator, targetBase, out remainder);

if (quotients.Count > 0 || quotient != 0) quotients.Add((byte) quotient);
}
var accumulator = source[i] + remainder * sourceBase;
var quotient = Math.DivRem(accumulator, targetBase, out remainder);

result.Insert(0, remainder);
source = quotients.ToArray();
if (quotients.Count > 0 || quotient != 0) quotients.Add((byte)quotient);
}

return result.Select(x => (byte) x).ToArray();
result.Insert(0, remainder);
source = quotients.ToArray();
}

return result.Select(x => (byte)x).ToArray();
}
}
23 changes: 12 additions & 11 deletions src/ScottBrady.IdentityModel/Extensions/ByteArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@

namespace ScottBrady.IdentityModel;

public static class ByteArrayExtensions
public static class ByteArrayExtensions // TODO: unit test
{
/// <summary>
/// Combines multiple byte arrays.
/// https://stackoverflow.com/questions/415291/best-way-to-combine-two-or-more-byte-arrays-in-c-sharp
/// </summary>
public static byte[] Combine(this byte[] source, params byte[][] arrays)
{
var output = new byte[source.Length + arrays.Sum(a => a.Length)];
Buffer.BlockCopy(source, 0, output, 0, source.Length);

var offset = source.Length;
foreach (var array in arrays) {
Buffer.BlockCopy(array, 0, output, offset, array.Length);
offset += array.Length;
}

return output;
var output = new byte[source.Length + arrays.Sum(a => a.Length)];
Buffer.BlockCopy(source, 0, output, 0, source.Length);

var offset = source.Length;
foreach (var array in arrays)
{
Buffer.BlockCopy(array, 0, output, offset, array.Length);
offset += array.Length;
}

return output;
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,57 @@
using Microsoft.IdentityModel.Tokens;
using System;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using ScottBrady.IdentityModel.Crypto;
using ScottBrady.IdentityModel.Tokens;

namespace ScottBrady.IdentityModel.Extensions
namespace ScottBrady.IdentityModel;

public static class ExtendedJsonWebKeyConverter
{
public static class ExtendedJsonWebKeyConverter
public static JsonWebKey ConvertFromEdDsaSecurityKey(EdDsaSecurityKey key)
{

var parameters = key.EdDsa.Parameters;
return new JsonWebKey
{
Crv = parameters.Curve,
X = parameters.X != null ? Base64UrlEncoder.Encode(parameters.X) : null,
D = parameters.D != null ? Base64UrlEncoder.Encode(parameters.D) : null,
Kty = ExtendedSecurityAlgorithms.KeyTypes.Ecdh,
Alg = ExtendedSecurityAlgorithms.EdDsa,
CryptoProviderFactory = key.CryptoProviderFactory,
};
}

public static bool TryConvertToEdDsaSecurityKey(JsonWebKey webKey, out EdDsaSecurityKey key)
{
public static JsonWebKey ConvertFromEdDsaSecurityKey(EdDsaSecurityKey securityKey)
key = null;

if (webKey != null
&& webKey.Kty == ExtendedSecurityAlgorithms.KeyTypes.Ecdh
&& webKey.Alg == ExtendedSecurityAlgorithms.EdDsa)
{
var parameters = securityKey.EdDsa.Parameters;
return new JsonWebKey
if (webKey.Crv == ExtendedSecurityAlgorithms.Curves.Ed25519
|| webKey.Crv == ExtendedSecurityAlgorithms.Curves.Ed448)
{
Crv = parameters.Curve,
X = parameters.X != null ? Base64UrlEncoder.Encode(parameters.X) : null,
D = parameters.D != null ? Base64UrlEncoder.Encode(parameters.D) : null,
Kty = ExtendedSecurityAlgorithms.KeyTypes.Ecdh,
Alg = ExtendedSecurityAlgorithms.EdDsa,
CryptoProviderFactory = securityKey.CryptoProviderFactory,
};
try
{
key = new EdDsaSecurityKey(EdDsa.Create(new EdDsaParameters(webKey.Crv)
{
X = webKey.X != null ? Base64UrlEncoder.DecodeBytes(webKey.X) : null,
D = webKey.D != null ? Base64UrlEncoder.DecodeBytes(webKey.D) : null
}));

return true;
}
catch (Exception ex)
{
LogHelper.LogWarning(LogHelper.FormatInvariant("Unable to create an EdDsaSecurityKey from the properties found in the JsonWebKey: '{0}', Exception '{1}'.", webKey, ex));
}

}
}

return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace ScottBrady.IdentityModel;

public static class StreamExtensions
public static class StreamExtensions // TODO: document and unit test
{
public static bool TryRead(this Stream stream, int length, out byte[] bytes)
{
Expand Down
11 changes: 2 additions & 9 deletions src/ScottBrady.IdentityModel/Tokens/EdDsa.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
Expand All @@ -10,6 +11,7 @@

namespace ScottBrady.IdentityModel.Tokens;

[UnsupportedOSPlatform("browser")]
public class EdDsa : AsymmetricAlgorithm
{
internal EdDsaParameters Parameters { get; private init; }
Expand Down Expand Up @@ -65,15 +67,6 @@ public static EdDsa Create(EdDsaParameters parameters)
};
}

/// <summary>
/// Create EdDSA from JSON Web Key (jwk).
/// </summary>
/// <param name="jwk">String containing JSON Web Key.</param>
public static EdDsa CreateFromJwk(string jwk)
{
throw new NotImplementedException();
}

public override string KeyExchangeAlgorithm => null;
public override string SignatureAlgorithm => ExtendedSecurityAlgorithms.EdDsa;
public override int KeySize => Parameters.D?.Length ?? Parameters.X?.Length ?? throw new InvalidOperationException("Missing EdDsa key");
Expand Down
4 changes: 2 additions & 2 deletions src/ScottBrady.IdentityModel/Tokens/EdDsaSecurityKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ private EdDsaSecurityKey()
CryptoProviderFactory.CustomCryptoProvider = new ExtendedCryptoProvider();
}

public EdDsaSecurityKey(EdDsa edDsaCreation) : this()
public EdDsaSecurityKey(EdDsa edDsa) : this()
{
EdDsa = edDsaCreation ?? throw new ArgumentNullException(nameof(edDsaCreation));
EdDsa = edDsa ?? throw new ArgumentNullException(nameof(edDsa));
}

[Obsolete("Deprecated in favor of EdDsa constructor")]
Expand Down
4 changes: 2 additions & 2 deletions src/ScottBrady.IdentityModel/Tokens/EdDsaSignatureProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal class EdDsaSignatureProvider : SignatureProvider
public EdDsaSignatureProvider(EdDsaSecurityKey key, string algorithm)
: base(key, algorithm)
{
edDsaKey = key;
}
edDsaKey = key;
}

protected override void Dispose(bool disposing) { }
public override byte[] Sign(byte[] input) => edDsaKey.EdDsa.Sign(input);
Expand Down
Loading

0 comments on commit f38a273

Please sign in to comment.