| File: System\Security\Cryptography\CngProperty.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography) |
// 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(); } /// <summary> /// Initializes a new instance of the <see cref="CngProperty" /> struct. /// </summary> /// <param name="name">The property name to set.</param> /// <param name="value">The property value to set.</param> /// <param name="options">A bitwise combination of the enumeration values that specify how the property is stored.</param> /// <exception cref="ArgumentNullException"><paramref name="name" /> is <see langword="null" />.</exception> public 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; } }