Skip to content

Commit

Permalink
Added tests for EdDsa as AsymmetricAlgorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
scottbrady91 committed Mar 30, 2024
1 parent af06726 commit 960e5c5
Show file tree
Hide file tree
Showing 14 changed files with 394 additions and 139 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ public static class ExtendedSecurityAlgorithms
// https://tools.ietf.org/html/rfc8037#section-5
public const string EdDsa = "EdDSA";

public class Curves
public static class Curves
{
// https://tools.ietf.org/html/rfc8037#section-5
public const string Ed25519 = "Ed25519";
public const string Ed448 = "Ed448";
public const string X25519 = "X25519";
public const string X448 = "X448";
}

public static class KeyTypes
{
// https://datatracker.ietf.org/doc/html/draft-ietf-jose-cfrg-curves-06#section-2
public const string Ecdh = "OKP";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static JsonWebKey ConvertFromEdDsaSecurityKey(EdDsaSecurityKey securityKe
Crv = parameters.Curve,
X = parameters.X != null ? Base64UrlEncoder.Encode(parameters.X) : null,
D = parameters.D != null ? Base64UrlEncoder.Encode(parameters.D) : null,
Kty = ExtendedJsonWebAlgorithmsKeyTypes.ECDH,
Kty = ExtendedSecurityAlgorithms.KeyTypes.Ecdh,
Alg = ExtendedSecurityAlgorithms.EdDsa,
CryptoProviderFactory = securityKey.CryptoProviderFactory,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<PackageTags>IdentityModel Base16 Base62 EdDSA</PackageTags>
<IncludeSymbols>true</IncludeSymbols>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>3.1.0</Version>
<Version>4.0.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<NoWarn>1591</NoWarn>
</PropertyGroup>
Expand Down
82 changes: 57 additions & 25 deletions src/ScottBrady.IdentityModel/Tokens/EdDsa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,59 @@

namespace ScottBrady.IdentityModel.Tokens;

public class EdDsa: AsymmetricAlgorithm
public class EdDsa : AsymmetricAlgorithm
{
internal EdDsaParameters Parameters { get; private init; }
internal AsymmetricKeyParameter PrivateKeyParameter { get; private init; }
internal AsymmetricKeyParameter PublicKeyParameter { get; private init; }

private EdDsa() { }

public static EdDsa Create(EdDsaParameters parameters)
{
if (parameters == null) throw new ArgumentNullException(nameof(parameters));

parameters.Validate();
return new EdDsa {Parameters = parameters};
return new EdDsa
{
Parameters = parameters,
PrivateKeyParameter = CreatePrivateKeyParameter(parameters),
PublicKeyParameter = CreatePublicKeyParameter(parameters)
};
}

/// <summary>
/// Create new key for EdDSA.
/// </summary>
/// <param name="curve">Create key for curve Ed25519 or Ed448.</param>
public static EdDsa Create(string curve)
public new static EdDsa Create(string curve)
{
if (string.IsNullOrWhiteSpace(curve)) throw new ArgumentNullException(nameof(curve));

IAsymmetricCipherKeyPairGenerator generator;
if (curve == ExtendedSecurityAlgorithms.Curves.Ed25519)
{
var generator = new Ed25519KeyPairGenerator();
generator = new Ed25519KeyPairGenerator();
generator.Init(new Ed25519KeyGenerationParameters(new SecureRandom()));
var keyPair = generator.GenerateKeyPair();
return new EdDsa {Parameters = new EdDsaParameters(keyPair, curve)};

}

if (curve == ExtendedSecurityAlgorithms.Curves.Ed448)
else if (curve == ExtendedSecurityAlgorithms.Curves.Ed448)
{
var generator = new Ed448KeyPairGenerator();
generator = new Ed448KeyPairGenerator();
generator.Init(new Ed448KeyGenerationParameters(new SecureRandom()));
var keyPair = generator.GenerateKeyPair();

return new EdDsa {Parameters = new EdDsaParameters(keyPair, curve)};
}

throw new NotSupportedException("Unsupported EdDSA curve");
else
{
throw new NotSupportedException("Unsupported EdDSA curve");
}

var keyPair = generator.GenerateKeyPair();
return new EdDsa
{
Parameters = new EdDsaParameters(keyPair, curve),
PrivateKeyParameter = keyPair.Private,
PublicKeyParameter = keyPair.Public
};
}

/// <summary>
Expand All @@ -61,12 +74,23 @@ 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");

public override KeySizes[] LegalKeySizes => Parameters.Curve switch
{
ExtendedSecurityAlgorithms.Curves.Ed25519 => new[] { new KeySizes(32, 32, 0) },
ExtendedSecurityAlgorithms.Curves.Ed448 => new[] { new KeySizes(57, 57, 0) },
_ => throw new NotSupportedException()
};

public byte[] Sign(byte[] input)
{
if (input == null) throw new ArgumentNullException(nameof(input));

var signer = CreateSigner();
signer.Init(true, CreatePrivateKeyParameter());
signer.Init(true, PrivateKeyParameter);
signer.BlockUpdate(input, 0, input.Length);

return signer.GenerateSignature();
Expand All @@ -89,28 +113,32 @@ public bool Verify(byte[] input, byte[] signature)
if (signature == null) throw new ArgumentNullException(nameof(signature));

var validator = CreateSigner();
validator.Init(false, CreatePublicKeyParameter());
validator.Init(false, PublicKeyParameter);
validator.BlockUpdate(input, 0, input.Length);

return validator.VerifySignature(signature);
}

private AsymmetricKeyParameter CreatePrivateKeyParameter()
private static AsymmetricKeyParameter CreatePrivateKeyParameter(EdDsaParameters parameters)
{
return Parameters.Curve switch
if (parameters.D == null) return null;

return parameters.Curve switch
{
ExtendedSecurityAlgorithms.Curves.Ed25519 => new Ed25519PrivateKeyParameters(Parameters.D, 0),
ExtendedSecurityAlgorithms.Curves.Ed448 => new Ed448PrivateKeyParameters(Parameters.D, 0),
ExtendedSecurityAlgorithms.Curves.Ed25519 => new Ed25519PrivateKeyParameters(parameters.D),
ExtendedSecurityAlgorithms.Curves.Ed448 => new Ed448PrivateKeyParameters(parameters.D),
_ => throw new NotSupportedException()
};
}

private AsymmetricKeyParameter CreatePublicKeyParameter()
private static AsymmetricKeyParameter CreatePublicKeyParameter(EdDsaParameters parameters)
{
return Parameters.Curve switch
if (parameters.X == null) return null;

return parameters.Curve switch
{
ExtendedSecurityAlgorithms.Curves.Ed25519 => new Ed25519PublicKeyParameters(Parameters.X, 0),
ExtendedSecurityAlgorithms.Curves.Ed448 => new Ed448PublicKeyParameters(Parameters.X, 0),
ExtendedSecurityAlgorithms.Curves.Ed25519 => new Ed25519PublicKeyParameters(parameters.X),
ExtendedSecurityAlgorithms.Curves.Ed448 => new Ed448PublicKeyParameters(parameters.X),
_ => throw new NotSupportedException()
};
}
Expand All @@ -124,4 +152,8 @@ private ISigner CreateSigner()
_ => throw new NotSupportedException()
};
}

public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<char> password) => throw new NotImplementedException();
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<byte> passwordBytes) => throw new NotImplementedException();
public override void ImportFromPem(ReadOnlySpan<char> input) => throw new NotImplementedException();
}
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 edDsa) : this()
public EdDsaSecurityKey(EdDsa edDsaCreation) : this()
{
EdDsa = edDsa ?? throw new ArgumentNullException(nameof(edDsa));
EdDsa = edDsaCreation ?? throw new ArgumentNullException(nameof(edDsaCreation));
}

[Obsolete("Deprecated in favor of EdDsa constructor")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System;
using System.Security.Cryptography;
using AutoFixture;
using FluentAssertions;
using ScottBrady.IdentityModel.Crypto;
using ScottBrady.IdentityModel.Tokens;
using Xunit;

namespace ScottBrady.IdentityModel.Tests.Tokens.EdDSA.AsymmetricAlgorithm;

public class EdDsaBaseClassTests : EdDsaTestBase
{
public static TheoryData<EdDsa, int> Keys
=> new TheoryData<EdDsa, int> { { _ed25519Key, 32 }, { _ed448Key, 57 } };

private static readonly Fixture _fixture = new();
private static readonly EdDsa _ed25519Key = EdDsa.Create(ExtendedSecurityAlgorithms.Curves.Ed25519);
private static readonly EdDsa _ed448Key = EdDsa.Create(ExtendedSecurityAlgorithms.Curves.Ed448);

[Theory, MemberData(nameof(Keys))]
public void KeySize_Expect32(EdDsa key, int expectedKeySize)
=> key.KeySize.Should().Be(expectedKeySize);

#pragma warning disable SYSLIB0045
[Fact]
public void Create_WhenEdDsaCurve_ExpectNull()
=> System.Security.Cryptography.AsymmetricAlgorithm.Create(ExtendedSecurityAlgorithms.EdDsa).Should().BeNull();
#pragma warning restore SYSLIB0045

#pragma warning disable SYSLIB0007
[Fact]
public void Create_ExpectPlatformNotSupportedException()
=> Assert.Throws<PlatformNotSupportedException>(EdDsa.Create);
#pragma warning restore SYSLIB0007

[Theory, MemberData(nameof(Keys))]
public void LegalKeySizes_ExpectCorrectValues(EdDsa key, int keySize)
=> key.LegalKeySizes.Should().BeEquivalentTo(new[] { new KeySizes(keySize, keySize, 0) });

[Theory, MemberData(nameof(Keys))]
public void SignatureAlgorithm_ExpectEdDSA(EdDsa key, int _)
=> key.SignatureAlgorithm.Should().Be(ExtendedSecurityAlgorithms.EdDsa);

[Theory, MemberData(nameof(Keys))]
public void KeyExchangeAlgorithm_ExpectNull(EdDsa key, int _)
=> key.KeyExchangeAlgorithm.Should().BeNull();

[Theory, MemberData(nameof(Keys))]
public void FromXmlString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.FromXmlString(""));

[Theory, MemberData(nameof(Keys))]
public void ToXmlString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ToXmlString(true));

[Theory, MemberData(nameof(Keys))]
public void ImportEncryptedPkcs8PrivateKey_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportEncryptedPkcs8PrivateKey(Array.Empty<byte>(), Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void ImportEncryptedPkcs8PrivateKey_WithPasswordString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportEncryptedPkcs8PrivateKey(Array.Empty<char>(), Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void ImportPkcs8PrivateKey_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportPkcs8PrivateKey(Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void ImportSubjectPublicKeyInfo_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportSubjectPublicKeyInfo(Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void ExportEncryptedPkcs8PrivateKey_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ExportEncryptedPkcs8PrivateKey(Array.Empty<byte>(), _fixture.Create<PbeParameters>()));

[Theory, MemberData(nameof(Keys))]
public void ExportEncryptedPkcs8PrivateKey_WithPasswordString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ExportEncryptedPkcs8PrivateKey(Array.Empty<char>(), _fixture.Create<PbeParameters>()));

[Theory, MemberData(nameof(Keys))]
public void ExportPkcs8PrivateKey_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(key.ExportPkcs8PrivateKey);

[Theory, MemberData(nameof(Keys))]
public void ExportSubjectPublicKeyInfo_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(key.ExportSubjectPublicKeyInfo);

[Theory, MemberData(nameof(Keys))]
public void TryExportEncryptedPkcs8PrivateKey_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportEncryptedPkcs8PrivateKey(Array.Empty<byte>(), _fixture.Create<PbeParameters>(), Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void TryExportEncryptedPkcs8PrivateKey_WithPasswordString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportEncryptedPkcs8PrivateKey(Array.Empty<char>(), _fixture.Create<PbeParameters>(), Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void TryExportPkcs8PrivateKey_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportPkcs8PrivateKey(Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void TryExportSubjectPublicKeyInfo_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportSubjectPublicKeyInfo(Array.Empty<byte>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void ImportFromEncryptedPem_WithPasswordString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportFromEncryptedPem(Array.Empty<char>(), Array.Empty<char>()));

[Theory, MemberData(nameof(Keys))]
public void ImportFromEncryptedPem_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportFromEncryptedPem(Array.Empty<char>(), Array.Empty<byte>()));

[Theory, MemberData(nameof(Keys))]
public void ImportFromPem_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ImportFromPem(Array.Empty<char>()));

#if NET8
[Theory, MemberData(nameof(Keys))]
public void ExportPkcs8PrivateKeyPem_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ExportPkcs8PrivateKeyPem());

[Theory, MemberData(nameof(Keys))]
public void ExportEncryptedPkcs8PrivateKeyPem_WithPasswordString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ExportEncryptedPkcs8PrivateKeyPem(Array.Empty<char>(), _fixture.Create<PbeParameters>()));

[Theory, MemberData(nameof(Keys))]
public void ExportEncryptedPkcs8PrivateKeyPem_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ExportEncryptedPkcs8PrivateKeyPem(Array.Empty<byte>(), _fixture.Create<PbeParameters>()));

[Theory, MemberData(nameof(Keys))]
public void ExportSubjectPublicKeyInfoPem_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.ExportSubjectPublicKeyInfoPem();

[Theory, MemberData(nameof(Keys))]
public void TryExportSubjectPublicKeyInfoPem_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportSubjectPublicKeyInfoPem(Array.Empty<char>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void TryExportPkcs8PrivateKeyPem_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportPkcs8PrivateKeyPem(Array.Empty<char>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void TryExportEncryptedPkcs8PrivateKeyPem_WithPasswordString_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportEncryptedPkcs8PrivateKeyPem(Array.Empty<char>(), _fixture.Create<PbeParameters>()), Array.Empty<char>(), out var _));

[Theory, MemberData(nameof(Keys))]
public void TryExportEncryptedPkcs8PrivateKeyPem_WithPasswordBytes_ExpectNotImplementedException(EdDsa key, int _)
=> Assert.Throws<NotImplementedException>(() => key.TryExportEncryptedPkcs8PrivateKeyPem(Array.Empty<byte>(), _fixture.Create<PbeParameters>()), Array.Empty<char>(), out var _));
#endif

[Theory, MemberData(nameof(Keys))]
public void Clear_WhenDisposed_ExpectNoException(EdDsa key, int _)
=> key.Clear();

[Theory, MemberData(nameof(Keys))]
public void Dispose_WhenDisposed_ExpectNoException(EdDsa key, int _)
=> key.Dispose();


}
Loading

0 comments on commit 960e5c5

Please sign in to comment.