// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.DataProtection { public unsafe class SecretTests { [Fact] public void Ctor_ArraySegment_Default_Throws() { // Act & assert ExceptionAssert.ThrowsArgument( testCode: () => new Secret(default(ArraySegment)), paramName: "array", exceptionMessage: null); } [Fact] public void Ctor_ArraySegment_Success() { // Arrange var input = new ArraySegment(new byte[] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }, 1, 3); // Act var secret = new Secret(input); input.Array[2] = 0xFF; // mutate original array - secret shouldn't be modified // Assert - length Assert.Equal(3, secret.Length); // Assert - managed buffer var outputSegment = new ArraySegment(new byte[7], 2, 3); secret.WriteSecretIntoBuffer(outputSegment); Assert.Equal(new byte[] { 0x20, 0x30, 0x40 }, outputSegment.AsStandaloneArray()); // Assert - unmanaged buffer var outputBuffer = new byte[3]; fixed (byte* pOutputBuffer = outputBuffer) { secret.WriteSecretIntoBuffer(pOutputBuffer, 3); } Assert.Equal(new byte[] { 0x20, 0x30, 0x40 }, outputBuffer); } [Fact] public void Ctor_Buffer_Success() { // Arrange var input = new byte[] { 0x20, 0x30, 0x40 }; // Act var secret = new Secret(input); input[1] = 0xFF; // mutate original array - secret shouldn't be modified // Assert - length Assert.Equal(3, secret.Length); // Assert - managed buffer var outputSegment = new ArraySegment(new byte[7], 2, 3); secret.WriteSecretIntoBuffer(outputSegment); Assert.Equal(new byte[] { 0x20, 0x30, 0x40 }, outputSegment.AsStandaloneArray()); // Assert - unmanaged buffer var outputBuffer = new byte[3]; fixed (byte* pOutputBuffer = outputBuffer) { secret.WriteSecretIntoBuffer(pOutputBuffer, 3); } Assert.Equal(new byte[] { 0x20, 0x30, 0x40 }, outputBuffer); } [Fact] public void Ctor_Buffer_ZeroLength_Success() { // Act var secret = new Secret(new byte[0]); // Assert - none of these methods should throw Assert.Equal(0, secret.Length); secret.WriteSecretIntoBuffer(new ArraySegment(new byte[0])); byte dummy; secret.WriteSecretIntoBuffer(&dummy, 0); } [Fact] public void Ctor_Pointer_WithNullPointer_ThrowsArgumentNull() { // Act & assert ExceptionAssert.ThrowsArgumentNull( testCode: () => new Secret(null, 0), paramName: "secret"); } [Fact] public void Ctor_Pointer_WithNegativeLength_ThrowsArgumentOutOfRange() { // Act & assert ExceptionAssert.ThrowsArgumentOutOfRange( testCode: () => { byte dummy; new Secret(&dummy, -1); }, paramName: "secretLength", exceptionMessage: Resources.Common_ValueMustBeNonNegative); } [Fact] public void Ctor_Pointer_ZeroLength_Success() { // Arrange byte input; // Act var secret = new Secret(&input, 0); // Assert - none of these methods should throw Assert.Equal(0, secret.Length); secret.WriteSecretIntoBuffer(new ArraySegment(new byte[0])); byte dummy; secret.WriteSecretIntoBuffer(&dummy, 0); } [Fact] public void Ctor_Pointer_Success() { // Arrange byte* input = stackalloc byte[3]; input[0] = 0x20; input[1] = 0x30; input[2] = 0x40; // Act var secret = new Secret(input, 3); input[1] = 0xFF; // mutate original buffer - secret shouldn't be modified // Assert - length Assert.Equal(3, secret.Length); // Assert - managed buffer var outputSegment = new ArraySegment(new byte[7], 2, 3); secret.WriteSecretIntoBuffer(outputSegment); Assert.Equal(new byte[] { 0x20, 0x30, 0x40 }, outputSegment.AsStandaloneArray()); // Assert - unmanaged buffer var outputBuffer = new byte[3]; fixed (byte* pOutputBuffer = outputBuffer) { secret.WriteSecretIntoBuffer(pOutputBuffer, 3); } Assert.Equal(new byte[] { 0x20, 0x30, 0x40 }, outputBuffer); } [Fact] public void Random_ZeroLength_Success() { // Act var secret = Secret.Random(0); // Assert Assert.Equal(0, secret.Length); } [Fact] public void Random_LengthIsMultipleOf16_Success() { // Act var secret = Secret.Random(32); // Assert Assert.Equal(32, secret.Length); Guid* pGuids = stackalloc Guid[2]; secret.WriteSecretIntoBuffer((byte*)pGuids, 32); Assert.NotEqual(Guid.Empty, pGuids[0]); Assert.NotEqual(Guid.Empty, pGuids[1]); Assert.NotEqual(pGuids[0], pGuids[1]); } [Fact] public void Random_LengthIsNotMultipleOf16_Success() { // Act var secret = Secret.Random(31); // Assert Assert.Equal(31, secret.Length); Guid* pGuids = stackalloc Guid[2]; secret.WriteSecretIntoBuffer((byte*)pGuids, 31); Assert.NotEqual(Guid.Empty, pGuids[0]); Assert.NotEqual(Guid.Empty, pGuids[1]); Assert.NotEqual(pGuids[0], pGuids[1]); Assert.Equal(0, ((byte*)pGuids)[31]); // last byte shouldn't have been overwritten } [Fact] public void WriteSecretIntoBuffer_ArraySegment_IncorrectlySizedBuffer_Throws() { // Arrange var secret = Secret.Random(16); // Act & assert ExceptionAssert.ThrowsArgument( testCode: () => secret.WriteSecretIntoBuffer(new ArraySegment(new byte[100])), paramName: "buffer", exceptionMessage: Resources.FormatCommon_BufferIncorrectlySized(100, 16)); } [Fact] public void WriteSecretIntoBuffer_ArraySegment_Disposed_Throws() { // Arrange var secret = Secret.Random(16); secret.Dispose(); // Act & assert Assert.Throws( testCode: () => secret.WriteSecretIntoBuffer(new ArraySegment(new byte[16]))); } [Fact] public void WriteSecretIntoBuffer_Pointer_NullBuffer_Throws() { // Arrange var secret = Secret.Random(16); // Act & assert ExceptionAssert.ThrowsArgumentNull( testCode: () => secret.WriteSecretIntoBuffer(null, 100), paramName: "buffer"); } [Fact] public void WriteSecretIntoBuffer_Pointer_IncorrectlySizedBuffer_Throws() { // Arrange var secret = Secret.Random(16); // Act & assert ExceptionAssert.ThrowsArgument( testCode: () => { byte* pBuffer = stackalloc byte[100]; secret.WriteSecretIntoBuffer(pBuffer, 100); }, paramName: "bufferLength", exceptionMessage: Resources.FormatCommon_BufferIncorrectlySized(100, 16)); } [Fact] public void WriteSecretIntoBuffer_Pointer_Disposed_Throws() { // Arrange var secret = Secret.Random(16); secret.Dispose(); // Act & assert Assert.Throws( testCode: () => { byte* pBuffer = stackalloc byte[16]; secret.WriteSecretIntoBuffer(pBuffer, 16); }); } } }