|
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Internal.Cryptography;
namespace System.Security.Cryptography
{
/// <summary>
/// Wrapper representing an arbitrary property of a CNG key or provider
/// </summary>
[StructLayout(LayoutKind.Sequential)] // The [StructLayout] is here to prevent a spurious ApiReviewer alert. We do not actually depend on the layout of this struct.
public struct CngProperty : IEquatable<CngProperty>
{
public CngProperty(string name, byte[]? value, CngPropertyOptions options)
: this()
{
ArgumentNullException.ThrowIfNull(name);
Name = name;
Options = options;
_lazyHashCode = default(int?);
_value = value.CloneByteArray();
}
internal CngProperty(string name, ReadOnlySpan<byte> value, CngPropertyOptions options)
: this()
{
ArgumentNullException.ThrowIfNull(name);
Name = name;
Options = options;
_lazyHashCode = default;
_value = value.ToArray();
}
/// <summary>
/// Name of the property
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Contents of the property
/// </summary>
/// <returns></returns>
public byte[]? GetValue() => _value.CloneByteArray();
/// <summary>
/// Options used to set / get the property
/// </summary>
public CngPropertyOptions Options { get; }
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is CngProperty && Equals((CngProperty)obj);
}
public bool Equals(CngProperty other)
{
//
// We will consider CNG properties equal only if the name, options and value are all also equal
//
if (!string.Equals(Name, other.Name, StringComparison.Ordinal))
return false;
if (Options != other.Options)
return false;
if (_value == null)
return other._value == null;
if (other._value == null)
return false;
return _value.AsSpan().SequenceEqual(other._value);
}
public override int GetHashCode()
{
if (!_lazyHashCode.HasValue)
{
int hashCode = Name.GetHashCode() ^ Options.GetHashCode();
// The hash code for a byte is just the value of that byte. Since this will only modify the
// lower bits of the hash code, we'll xor each byte into different sections of the hash code
if (_value != null)
{
for (int i = 0; i < _value.Length; i++)
{
// Shift each byte forward by one byte, so that every 4 bytes has to potential to update
// each of the calculated hash code's bytes.
int shifted = (int)(_value[i] << ((i % 4) * 8));
hashCode ^= shifted;
}
}
_lazyHashCode = hashCode;
}
return _lazyHashCode.Value;
}
public static bool operator ==(CngProperty left, CngProperty right)
{
return left.Equals(right);
}
public static bool operator !=(CngProperty left, CngProperty right)
{
return !left.Equals(right);
}
internal byte[]? GetValueWithoutCopying()
{
return _value;
}
private readonly byte[]? _value;
private int? _lazyHashCode;
}
}
|