| File: System\Security\Cryptography\Cose\CoseHeaderValue.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography.Cose\src\System.Security.Cryptography.Cose.csproj (System.Security.Cryptography.Cose) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; using System.Formats.Cbor; namespace System.Security.Cryptography.Cose { /// <summary> /// Represents a COSE header value. /// </summary> public readonly struct CoseHeaderValue : IEquatable<CoseHeaderValue> { /// <summary> /// Gets the CBOR-encoded value of this instance. /// </summary> /// <value>A view of the CBOR-encoded value as a contiguous region of memory.</value> public readonly ReadOnlyMemory<byte> EncodedValue { get; } private CoseHeaderValue(ReadOnlyMemory<byte> encodedValue) { EncodedValue = encodedValue; } private static CoseHeaderValue FromEncodedValue(ReadOnlyMemory<byte> encodedValue) { // We don't validate here as we need to know in which label the value is going to be used to validate even more semantics. CoseHeaderValue value = new CoseHeaderValue(encodedValue); return value; } /// <summary> /// Creates a <see cref="CoseHeaderValue"/> instance from a CBOR-encoded value. /// </summary> /// <param name="encodedValue">A CBOR-encoded value to represent.</param> /// <returns>An instance that represents the encoded value.</returns> public static CoseHeaderValue FromEncodedValue(ReadOnlySpan<byte> encodedValue) { var encodedValueCopy = new ReadOnlyMemory<byte>(encodedValue.ToArray()); return FromEncodedValue(encodedValueCopy); } /// <summary> /// Creates a <see cref="CoseHeaderValue"/> instance from a CBOR-encoded value. /// </summary> /// <param name="encodedValue">A CBOR-encoded value to represent.</param> /// <returns>An instance that represents the encoded value.</returns> /// <exception cref="ArgumentNullException"><paramref name="encodedValue"/> is <see langword="null"/>.</exception> public static CoseHeaderValue FromEncodedValue(byte[] encodedValue) { ArgumentNullException.ThrowIfNull(encodedValue); return FromEncodedValue(encodedValue.AsSpan()); } private static ReadOnlyMemory<byte> Encode(CborWriter writer) { byte[] buffer = new byte[writer.BytesWritten]; writer.Encode(buffer); return buffer.AsMemory(); } /// <summary> /// Creates a <see cref="CoseHeaderValue"/> instance from a signed integer. /// </summary> /// <param name="value">The value to represent.</param> /// <returns>An instance that represents the specified value.</returns> public static CoseHeaderValue FromInt32(int value) { var writer = new CborWriter(); writer.WriteInt32(value); return FromEncodedValue(Encode(writer)); } /// <summary> /// Creates a <see cref="CoseHeaderValue"/> instance from a string. /// </summary> /// <param name="value">The value to represent.</param> /// <returns>An instance that represents the specified value.</returns> /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception> public static CoseHeaderValue FromString(string value) { ArgumentNullException.ThrowIfNull(value); var writer = new CborWriter(); writer.WriteTextString(value); return FromEncodedValue(Encode(writer)); } /// <summary> /// Creates a <see cref="CoseHeaderValue"/> instance from a span of bytes. /// </summary> /// <param name="value">The bytes to be encoded and that the instance will represent.</param> /// <returns>An instance that represents the CBOR-encoded <paramref name="value"/>.</returns> /// <seealso cref="FromEncodedValue(ReadOnlySpan{byte})"/> public static CoseHeaderValue FromBytes(ReadOnlySpan<byte> value) { var writer = new CborWriter(); writer.WriteByteString(value); return FromEncodedValue(Encode(writer)); } /// <summary> /// Creates a <see cref="CoseHeaderValue"/> instance from a byte array. /// </summary> /// <param name="value">The bytes to be encoded and that the instance will represent.</param> /// <returns>An instance that represents the CBOR-encoded <paramref name="value"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception> /// <seealso cref="FromEncodedValue(byte[])"/> public static CoseHeaderValue FromBytes(byte[] value) { ArgumentNullException.ThrowIfNull(value); return FromBytes(value.AsSpan()); } /// <summary> /// Gets the value as a signed integer. /// </summary> /// <returns>The value as a signed integer.</returns> /// <exception cref="InvalidOperationException">The value could not be decoded as a 32-bit signed integer.</exception> public int GetValueAsInt32() { var reader = new CborReader(EncodedValue); int retVal; try { retVal = reader.ReadInt32(); } catch (Exception ex) when (ex is CborContentException or InvalidOperationException or OverflowException) { throw new InvalidOperationException(SR.CoseHeaderValueErrorWhileDecoding, ex); } if (reader.BytesRemaining != 0) { throw new InvalidOperationException(SR.CoseHeaderMapCborEncodedValueNotValid); } return retVal; } /// <summary> /// Gets the value as a text string. /// </summary> /// <returns>The value as a text string.</returns> /// <exception cref="InvalidOperationException">The value could not be decoded as text string.</exception> public string GetValueAsString() { var reader = new CborReader(EncodedValue); string retVal; try { retVal = reader.ReadTextString(); } catch (Exception ex) when (ex is CborContentException or InvalidOperationException) { throw new InvalidOperationException(SR.CoseHeaderValueErrorWhileDecoding, ex); } if (reader.BytesRemaining != 0) { throw new InvalidOperationException(SR.CoseHeaderMapCborEncodedValueNotValid); } return retVal; } /// <summary> /// Gets the CBOR-encoded value as a byte string. /// </summary> /// <returns>The decoded value as a byte array.</returns> /// <exception cref="InvalidOperationException">The value could not be decoded as byte string.</exception> public byte[] GetValueAsBytes() { var reader = new CborReader(EncodedValue); byte[] retVal; try { retVal = reader.ReadByteString(); } catch (Exception ex) when (ex is CborContentException or InvalidOperationException) { throw new InvalidOperationException(SR.CoseHeaderValueErrorWhileDecoding, ex); } if (reader.BytesRemaining != 0) { throw new InvalidOperationException(SR.CoseHeaderMapCborEncodedValueNotValid); } return retVal; } /// <summary> /// Gets the CBOR-encoded value as a byte string. /// </summary> /// <param name="destination">The buffer in which to write the decoded value.</param> /// <returns>The number of bytes written to <paramref name="destination"/>.</returns> /// <exception cref="ArgumentException"><paramref name="destination"/> is too small to hold the value.</exception> /// <exception cref="InvalidOperationException">The value could not be decoded as byte string.</exception> public int GetValueAsBytes(Span<byte> destination) { var reader = new CborReader(EncodedValue); int bytesWritten; try { if (!reader.TryReadByteString(destination, out bytesWritten)) { throw new ArgumentException(SR.Argument_DestinationTooSmall, nameof(destination)); } } catch (Exception ex) when (ex is CborContentException or InvalidOperationException) { throw new InvalidOperationException(SR.CoseHeaderValueErrorWhileDecoding, ex); } if (reader.BytesRemaining != 0) { throw new InvalidOperationException(SR.CoseHeaderMapCborEncodedValueNotValid); } return bytesWritten; } /// <summary> /// Returns a value indicating whether this instance is equal to the specified instance. /// </summary> /// <param name="obj">The object to compare to this instance.</param> /// <returns><see langword="true"/> if the value parameter equals the value of this instance; otherwise, <see langword="false"/>.</returns> public override bool Equals([NotNullWhen(true)] object? obj) => obj is CoseHeaderValue otherObj && Equals(otherObj); /// <summary> /// Returns a value indicating whether this instance is equal to a specified object. /// </summary> /// <param name="other">The object to compare to this instance.</param> /// <returns><see langword="true"/> if value is an instance of <see cref="CoseHeaderValue"/> and equals the value of this instance; otherwise, <see langword="false"/>.</returns> public bool Equals(CoseHeaderValue other) => EncodedValue.Span.SequenceEqual(other.EncodedValue.Span); /// <summary> /// Returns the hash code for this instance. /// </summary> /// <returns>A 32-bit signed integer hash code.</returns> public override int GetHashCode() { HashCode hashCode = default; hashCode.AddBytes(EncodedValue.Span); return hashCode.ToHashCode(); } /// <summary> /// Determines whether two specified header value instances are equal. /// </summary> /// <param name="left">The first object to compare.</param> /// <param name="right">The second object to compare.</param> /// <returns><see langword="true"/> if left and right represent the same value; otherwise, <see langword="false"/>.</returns> public static bool operator ==(CoseHeaderValue left, CoseHeaderValue right) => left.Equals(right); /// <summary> /// Determines whether two specified header value instances are not equal. /// </summary> /// <param name="left">The first object to compare.</param> /// <param name="right">The second object to compare.</param> /// <returns><see langword="true"/> if left and right do not represent the same value; otherwise, <see langword="false"/>.</returns> public static bool operator !=(CoseHeaderValue left, CoseHeaderValue right) => !left.Equals(right); } }