Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacing Yubico.DotnetPolyFills with PolySharp. Bumping LanguageVersion to C#12. #132

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ file_header_template = Copyright 2024 Yubico AB\n\nLicensed under the Apache Lic
# C# files
[*.cs]

# Use concrete types when possible for improved performance
dotnet_diagnostic.CA1859.severity = suggestion

#### Core EditorConfig Options ####

# We can't do this until C# 9.
Expand Down Expand Up @@ -338,6 +341,9 @@ csharp_style_var_for_built_in_types = true:suggestion
csharp_style_unused_value_expression_statement_preference = unused_local_variable:none
resharper_for_simple_types = use_var

# CA1860: Avoid using 'Enumerable.Any()' extension method
dotnet_diagnostic.CA1860.severity = suggestion

# CA1707: Identifiers should not contain underscores
dotnet_diagnostic.ca1707.severity = none

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json

global-json-file: "./global.json"
dotnet-version: 6.0.300
- name: Add local NuGet repository
run: dotnet nuget add source --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/Yubico/index.json"

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
global-json-file: "./global.json"
dotnet-version: 6.0.300

- name: Add local NuGet repository
run: dotnet nuget add source --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/Yubico/index.json"
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
global-json-file: "./global.json"
dotnet-version: 6.0.300

- name: Add local NuGet repository
run: dotnet nuget add source --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/Yubico/index.json"
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
global-json-file: "./global.json"
dotnet-version: 6.0.300

- name: Add local NuGet repository
run: dotnet nuget add source --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/Yubico/index.json"

Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/verify-code-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ on:
jobs:
verify-code-style:
name: "Verify code style"
runs-on: windows-latest
runs-on: windows-2019

steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
global-json-file: "./global.json"
dotnet-version: 6.0.300

- name: Add local NuGet repository
run: dotnet nuget add source --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/Yubico/index.json"
#- name: Build Yubico.NET.SDK.sln
Expand Down
12 changes: 11 additions & 1 deletion Yubico.Core/src/Yubico.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,19 @@ limitations under the License. -->


<ItemGroup>
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1"/>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="PolySharp" Version="1.14.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0"/>
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
<!-- The wildcard version tag allows us to include the latest minor and pre-release versions -->
<PackageReference Include="Yubico.NativeShims" Version="1.*-*" />
<ProjectReference Include="..\..\Yubico.DotNetPolyfills\src\Yubico.DotNetPolyfills.csproj" />
<!-- <ProjectReference Include="..\..\Yubico.DotNetPolyfills\src\Yubico.DotNetPolyfills.csproj" />-->
</ItemGroup>

<ItemGroup Label="Expose internal test hooks to Unit Test projects">
Expand All @@ -133,4 +139,8 @@ limitations under the License. -->
</AssemblyAttribute>

</ItemGroup>

<ItemGroup>
<Reference Include="System.Security" Condition="'$(TargetFramework)' == 'net47'"/>
</ItemGroup>
</Project>
4 changes: 3 additions & 1 deletion Yubico.Core/src/Yubico/Core/Buffers/MultiString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace Yubico.Core.Buffers
/// </summary>
public static class MultiString
{
private static readonly char[] _separator = ['\0'];

/// <summary>
/// Converts the byte array representing a multi-null-terminated string and return them as
/// .NET strings.
Expand All @@ -40,7 +42,7 @@ public static string[] GetStrings(byte[] value, Encoding encoding)

return encoding
.GetString(value)
.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
.Split(_separator, StringSplitOptions.RemoveEmptyEntries);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

namespace Yubico.Core.Cryptography;

/// <summary>
/// Source:
/// https://github.com/dotnet/runtime/blob/16b456426dfb5212a24bfb78bfd5d9adfcc95185/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicOperations.cs
/// </summary>
public static class CryptographicOperations
{
/// <summary>
/// Determine 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>
/// <c>true</c> if <paramref name="left" /> and <paramref name="right" /> have the same
/// values for <see cref="ReadOnlySpan{T}.Length" /> and the same contents, <c>false</c>
/// otherwise.
/// </returns>
/// <remarks>
/// This method compares two buffers' contents for equality in a manner which does not
/// leak timing information, making it ideal for use within cryptographic routines.
/// This method will short-circuit and return <c>false</c> only if <paramref name="left" />
/// and <paramref name="right" /> have different lengths.
/// Fixed-time behavior is guaranteed in all other cases, including if <paramref name="left" />
/// and <paramref name="right" /> reference the same address.
/// </remarks>
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool FixedTimeEquals(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
{
// NoOptimization because we want this method to be exactly as non-short-circuiting
// as written.
//
// NoInlining because the NoOptimization would get lost if the method got inlined.

if (left.Length != right.Length)
{
return false;
}

int length = left.Length;
int accum = 0;

for (int i = 0; i < length; i++)
{
accum |= left[i] - right[i];
}

return accum == 0;
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]

// NoOptimize to prevent the optimizer from deciding this call is unnecessary
// NoInlining to prevent the inliner from forgetting that the method was no-optimize
public static void ZeroMemory(Span<byte> buffer) => buffer.Clear();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

using System;
using System.Globalization;
using System.Security.Cryptography;
using Yubico.Core.Buffers;
using Yubico.Core.Cryptography;
using Yubico.Core.Logging;
using Yubico.PlatformInterop;

Expand Down
2 changes: 2 additions & 0 deletions Yubico.Core/src/Yubico/Core/Devices/Hid/WindowsHidDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ private void ResolveIdsFromInstancePath(string instancePath)
// 012345678901234567890123456789
// ^--- ^---

#pragma warning disable CA1862
if (instancePath.ToUpperInvariant().Contains("VID") && instancePath.ToUpperInvariant().Contains("HID"))
#pragma warning restore CA1862
{
// If this fails, vendorId will be 0.
_ = TryGetHexShort(instancePath, 12, 4, out ushort vendorId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ namespace Yubico.Core.Devices.SmartCard
internal class DesktopSmartCardDeviceListener : SmartCardDeviceListener, IDisposable
{
private readonly Logger _log = Log.GetLogger();

// The resource manager context.
private SCardContext _context;

// The active smart card readers.
private SCARD_READER_STATE[] _readerStates;

private bool _isListening;
private Thread? _listenerThread;
private bool _isListening;
private bool _disposedValue; // To detect redundant calls
private static readonly string[] _readerNames = ["\\\\?\\Pnp\\Notifications"];

/// <summary>
/// Constructs a <see cref="SmartCardDeviceListener"/>.
Expand Down Expand Up @@ -74,16 +75,19 @@ public DesktopSmartCardDeviceListener()
/// </summary>
private void StartListening()
{
if (!_isListening)
if (_isListening)
{
_listenerThread = new Thread(ListenForReaderChanges)
{
IsBackground = true
};
_isListening = true;
Status = DeviceListenerStatus.Started;
_listenerThread.Start();
return;
}

_listenerThread = new Thread(ListenForReaderChanges)
{
IsBackground = true
};

_isListening = true;
Status = DeviceListenerStatus.Started;
_listenerThread.Start();
}

// This method is the delegate sent to the new Thread.
Expand All @@ -104,25 +108,26 @@ private void ListenForReaderChanges()
}

#region IDisposable Support

private bool _disposedValue; // To detect redundant calls


/// <summary>
/// Disposes the objects.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
if (_disposedValue)
{
if (disposing)
{
uint _ = SCardCancel(_context);
_context.Dispose();
StopListening();
}
_disposedValue = true;
return;
}

if (disposing)
{
uint _ = SCardCancel(_context);
_context.Dispose();
StopListening();
}

_disposedValue = true;
}

~DesktopSmartCardDeviceListener()
Expand Down Expand Up @@ -192,7 +197,7 @@ private bool CheckForUpdates(int timeout, bool usePnpWorkaround)
SCARD_READER_STATE[] removedReaderStates = newStates.Except(eventStateList, new ReaderStateComparer()).ToArray();

// Don't get status changes if there are no updates in state list.
if (!addedReaderStates.Any() && !removedReaderStates.Any())
if (addedReaderStates.Length == 0 && removedReaderStates.Length == 0)
{
break;
}
Expand All @@ -214,7 +219,7 @@ private bool CheckForUpdates(int timeout, bool usePnpWorkaround)
// Only call get status change if a new reader was added. If nothing was added,
// we would otherwise hang / timeout here because all changes (in SCard's mind)
// have been dealt with.
if (addedReaderStates.Any())
if (addedReaderStates.Length != 0)
{
_log.LogInformation("Additional smart card readers were found. Calling GetStatusChange for more information.");
getStatusChangeResult = SCardGetStatusChange(_context, 0, updatedStates, updatedStates.Length);
Expand Down Expand Up @@ -280,7 +285,7 @@ private bool CheckForUpdates(int timeout, bool usePnpWorkaround)
// us of the change.
private bool UsePnpWorkaround()
{
SCARD_READER_STATE[] testState = SCARD_READER_STATE.CreateFromReaderNames(new[] { "\\\\?\\Pnp\\Notifications" });
SCARD_READER_STATE[] testState = SCARD_READER_STATE.CreateFromReaderNames(_readerNames);
_ = SCardGetStatusChange(_context, 0, testState, testState.Length);
bool usePnpWorkaround = testState[0].EventState.HasFlag(SCARD_STATE.UNKNOWN);
return usePnpWorkaround;
Expand Down
2 changes: 1 addition & 1 deletion Yubico.Core/src/Yubico/Core/Tlv/TlvSubElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

using System;
using System.Security.Cryptography;
using Yubico.Core.Cryptography;

namespace Yubico.Core.Tlv
{
Expand Down
13 changes: 7 additions & 6 deletions Yubico.Core/src/Yubico/PlatformInterop/SdkPlatformInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,19 @@ public static SdkPlatform OperatingSystem
{
return SdkPlatform.Windows;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ||
RuntimeInformation.IsOSPlatform(OSPlatform.Create("MACCATALYST")))
{
return SdkPlatform.MacOS;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return SdkPlatform.Linux;
}
else
{
return SdkPlatform.Unknown;
}

return SdkPlatform.Unknown;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,7 @@ private void ResolveHidUsages()

private static IList<string> GetDevicePaths(Guid classGuid, string? deviceInstanceId)
{
CmErrorCode errorCode;

errorCode = CM_Get_Device_Interface_List_Size(
CmErrorCode errorCode = CM_Get_Device_Interface_List_Size(
out int bufferLength,
classGuid,
deviceInstanceId,
Expand Down
Loading
Loading