Browse Source

Replace Portable.BouncyCastle by BouncyCastle.Cryptography

pull/1581/head
Kévin Chalet 3 years ago
parent
commit
cc121c061d
  1. 5
      Directory.Packages.props
  2. 49
      shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
  3. 6
      src/OpenIddict.Client/OpenIddict.Client.csproj
  4. 54
      src/OpenIddict.Client/OpenIddictClientHandlers.cs
  5. 14
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  6. 6
      src/OpenIddict.Core/OpenIddict.Core.csproj
  7. 6
      src/OpenIddict.Server/OpenIddict.Server.csproj
  8. 13
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

5
Directory.Packages.props

@ -41,7 +41,7 @@
<PackageVersion Include="MongoDB.Bson" Version="2.11.6" />
<PackageVersion Include="MongoDB.Driver" Version="2.11.6" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.0.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" />
<PackageVersion Include="SmartFormat" Version="3.2.0" />
<PackageVersion Include="System.Collections.Immutable" Version="1.7.1" />
@ -117,7 +117,6 @@
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="6.25.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="6.25.0" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" />
<PackageVersion Include="SmartFormat" Version="3.2.0" />
<PackageVersion Include="System.Net.Http.Json" Version="3.2.1" />
@ -326,7 +325,7 @@
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="6.25.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="6.25.0" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.0.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.5.0" />
<PackageVersion Include="SmartFormat" Version="3.2.0" />
<PackageVersion Include="System.Collections.Immutable" Version="1.7.1" />

49
shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs

@ -1,5 +1,5 @@
using System;
using System.Diagnostics;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
@ -620,6 +620,51 @@ internal static class OpenIddictHelpers
}
}
/// <summary>
/// Determines the equality of two byte sequences in an amount of time
/// which depends on the length of the sequences, but not the values.
/// </summary>
/// <param name="left">The first buffer to compare.</param>
/// <param name="right">The second buffer to compare.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="left"/> and <paramref name="right"/> have the same values
/// for <see cref="ReadOnlySpan{T}.Length"/> and the same contents, <see langword="false"/> otherwise.
/// </returns>
#if !SUPPORTS_TIME_CONSTANT_COMPARISONS
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
#endif
public static bool FixedTimeEquals(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
{
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
return CryptographicOperations.FixedTimeEquals(left, right);
#else
// Note: the logic used here is directly taken from the official implementation of
// the CryptographicOperations.FixedTimeEquals() method introduced in .NET Core 2.1.
//
// See https://github.com/dotnet/corefx/pull/27103 for more information.
// Note: these null checks can be theoretically considered as early checks
// (which would defeat the purpose of a time-constant comparison method),
// but the expected string length is the only information an attacker
// could get at this stage, which is not critical where this method is used.
if (left.Length != right.Length)
{
return false;
}
var length = left.Length;
var accumulator = 0;
for (var index = 0; index < length; index++)
{
accumulator |= left[index] - right[index];
}
return accumulator is 0;
#endif
}
#if SUPPORTS_KEY_DERIVATION_WITH_SPECIFIED_HASH_ALGORITHM
/// <summary>
/// Creates a derived key based on the specified <paramref name="secret"/> using PBKDF2.

6
src/OpenIddict.Client/OpenIddict.Client.csproj

@ -23,12 +23,6 @@ To use the client feature on ASP.NET Core or OWIN/Katana, reference the OpenIddi
<PackageReference Include="Microsoft.IdentityModel.Protocols" />
</ItemGroup>
<ItemGroup
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETFramework') Or
('$(TargetFrameworkIdentifier)' == '.NETStandard' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '2.1'))) ">
<PackageReference Include="Portable.BouncyCastle" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>

54
src/OpenIddict.Client/OpenIddictClientHandlers.cs

@ -9,17 +9,12 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions;
using static OpenIddict.Abstractions.OpenIddictExceptions;
#if !SUPPORTS_TIME_CONSTANT_COMPARISONS
using Org.BouncyCastle.Utilities;
#endif
namespace OpenIddict.Client;
[EditorBrowsable(EditorBrowsableState.Never)]
@ -621,16 +616,9 @@ public static partial class OpenIddictClientHandlers
//
// In any case, the authentication demand MUST be rejected as it's impossible to ensure
// it's not an injection or session fixation attack without the correct "rfp" value.
if (string.IsNullOrEmpty(context.RequestForgeryProtection) ||
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
!CryptographicOperations.FixedTimeEquals(
left: MemoryMarshal.AsBytes(comparand.AsSpan()),
right: MemoryMarshal.AsBytes(context.RequestForgeryProtection.AsSpan())))
#else
!Arrays.ConstantTimeAreEqual(
a: MemoryMarshal.AsBytes(comparand.AsSpan()).ToArray(),
b: MemoryMarshal.AsBytes(context.RequestForgeryProtection.AsSpan()).ToArray()))
#endif
if (string.IsNullOrEmpty(context.RequestForgeryProtection) || !OpenIddictHelpers.FixedTimeEquals(
left: MemoryMarshal.AsBytes(comparand.AsSpan()),
right: MemoryMarshal.AsBytes(context.RequestForgeryProtection.AsSpan())))
{
context.Logger.LogWarning(SR.GetResourceString(SR.ID6209));
@ -1517,17 +1505,10 @@ public static partial class OpenIddictClientHandlers
// If the two nonces don't match, return an error.
case { FrontchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
!CryptographicOperations.FixedTimeEquals(
!OpenIddictHelpers.FixedTimeEquals(
left: MemoryMarshal.AsBytes(left.AsSpan()), // The nonce in the identity token is already hashed.
right: MemoryMarshal.AsBytes(Base64UrlEncoder.Encode(
OpenIddictHelpers.ComputeSha256Hash(Encoding.UTF8.GetBytes(right))).AsSpan())):
#else
!Arrays.ConstantTimeAreEqual(
a: MemoryMarshal.AsBytes(left.AsSpan()).ToArray(), // The nonce in the identity token is already hashed.
b: MemoryMarshal.AsBytes(Base64UrlEncoder.Encode(
OpenIddictHelpers.ComputeSha256Hash(Encoding.UTF8.GetBytes(right))).AsSpan()).ToArray()):
#endif
context.Logger.LogWarning(SR.GetResourceString(SR.ID6210));
context.Reject(
@ -1658,15 +1639,10 @@ public static partial class OpenIddictClientHandlers
}
static bool ValidateTokenHash(string algorithm, string token, string hash) =>
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
CryptographicOperations.FixedTimeEquals(
OpenIddictHelpers.FixedTimeEquals(
left: MemoryMarshal.AsBytes(hash.AsSpan()),
right: MemoryMarshal.AsBytes(ComputeTokenHash(algorithm, token)));
#else
Arrays.ConstantTimeAreEqual(
a: MemoryMarshal.AsBytes(hash.AsSpan()).ToArray(),
b: MemoryMarshal.AsBytes(ComputeTokenHash(algorithm, token)).ToArray());
#endif
return default;
}
}
@ -2831,17 +2807,10 @@ public static partial class OpenIddictClientHandlers
// If the two nonces don't match, return an error.
case { BackchannelIdentityTokenNonce: string left, StateTokenNonce: string right } when
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
!CryptographicOperations.FixedTimeEquals(
!OpenIddictHelpers.FixedTimeEquals(
left: MemoryMarshal.AsBytes(left.AsSpan()), // The nonce in the identity token is already hashed.
right: MemoryMarshal.AsBytes(Base64UrlEncoder.Encode(
OpenIddictHelpers.ComputeSha256Hash(Encoding.UTF8.GetBytes(right))).AsSpan())):
#else
!Arrays.ConstantTimeAreEqual(
a: MemoryMarshal.AsBytes(left.AsSpan()).ToArray(), // The nonce in the identity token is already hashed.
b: MemoryMarshal.AsBytes(Base64UrlEncoder.Encode(
OpenIddictHelpers.ComputeSha256Hash(Encoding.UTF8.GetBytes(right))).AsSpan()).ToArray()):
#endif
context.Logger.LogWarning(SR.GetResourceString(SR.ID6211));
context.Reject(
@ -2936,15 +2905,10 @@ public static partial class OpenIddictClientHandlers
}
static bool ValidateTokenHash(string algorithm, string token, string hash) =>
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
CryptographicOperations.FixedTimeEquals(
OpenIddictHelpers.FixedTimeEquals(
left: MemoryMarshal.AsBytes(hash.AsSpan()),
right: MemoryMarshal.AsBytes(ComputeTokenHash(algorithm, token)));
#else
Arrays.ConstantTimeAreEqual(
a: MemoryMarshal.AsBytes(hash.AsSpan()).ToArray(),
b: MemoryMarshal.AsBytes(ComputeTokenHash(algorithm, token)).ToArray());
#endif
return default;
}
}

14
src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs

@ -25,10 +25,6 @@ using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
#endif
#if !SUPPORTS_TIME_CONSTANT_COMPARISONS
using Org.BouncyCastle.Utilities;
#endif
namespace OpenIddict.Core;
/// <summary>
@ -1511,15 +1507,9 @@ public class OpenIddictApplicationManager<TApplication> : IOpenIddictApplication
return false;
}
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
return CryptographicOperations.FixedTimeEquals(
left: payload.Slice(13 + salt.Length, keyLength),
return OpenIddictHelpers.FixedTimeEquals(
left: payload.Slice(13 + salt.Length, keyLength),
right: DeriveKey(secret, salt.ToArray(), algorithm, iterations, keyLength));
#else
return Arrays.ConstantTimeAreEqual(
a: payload.Slice(13 + salt.Length, keyLength).ToArray(),
b: DeriveKey(secret, salt.ToArray(), algorithm, iterations, keyLength));
#endif
}
}

6
src/OpenIddict.Core/OpenIddict.Core.csproj

@ -24,9 +24,9 @@
</ItemGroup>
<ItemGroup
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETFramework') Or
('$(TargetFrameworkIdentifier)' == '.NETStandard' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '2.1'))) ">
<PackageReference Include="Portable.BouncyCastle" />
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETFramework' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '4.7.2'))) Or
('$(TargetFrameworkIdentifier)' == '.NETStandard' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '2.1'))) ">
<PackageReference Include="BouncyCastle.Cryptography" />
</ItemGroup>
<ItemGroup>

6
src/OpenIddict.Server/OpenIddict.Server.csproj

@ -21,12 +21,6 @@ To use the server feature on ASP.NET Core or OWIN/Katana, reference the OpenIddi
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
</ItemGroup>
<ItemGroup
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETFramework') Or
('$(TargetFrameworkIdentifier)' == '.NETStandard' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '2.1'))) ">
<PackageReference Include="Portable.BouncyCastle" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>

13
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -8,7 +8,6 @@ using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -16,10 +15,6 @@ using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions;
#if !SUPPORTS_TIME_CONSTANT_COMPARISONS
using Org.BouncyCastle.Utilities;
#endif
namespace OpenIddict.Server;
public static partial class OpenIddictServerHandlers
@ -1562,15 +1557,9 @@ public static partial class OpenIddictServerHandlers
// Compare the verifier and the code challenge: if the two don't match, return an error.
// Note: to prevent timing attacks, a time-constant comparer is always used.
#if SUPPORTS_TIME_CONSTANT_COMPARISONS
if (!CryptographicOperations.FixedTimeEquals(
if (!OpenIddictHelpers.FixedTimeEquals(
left: MemoryMarshal.AsBytes(comparand.AsSpan()),
right: MemoryMarshal.AsBytes(challenge.AsSpan())))
#else
if (!Arrays.ConstantTimeAreEqual(
a: MemoryMarshal.AsBytes(comparand.AsSpan()).ToArray(),
b: MemoryMarshal.AsBytes(challenge.AsSpan()).ToArray()))
#endif
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6092), Parameters.CodeVerifier);

Loading…
Cancel
Save