// See the LICENSE file in the project root for more information.
using System;
-using System.Buffers;
using System.Diagnostics;
using System.Runtime.InteropServices;
-using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Win32.SafeHandles;
return GetDynamicBuffer((ptr, buf, i) => GetAsn1StringBytes(ptr, buf, i), asn1);
}
- internal static ArraySegment<byte> RentAsn1StringBytes(IntPtr asn1)
- {
- return RentDynamicBuffer((ptr, buf, i) => GetAsn1StringBytes(ptr, buf, i), asn1);
- }
-
internal static byte[] GetX509Thumbprint(SafeX509Handle x509)
{
return GetDynamicBuffer((handle, buf, i) => GetX509Thumbprint(handle, buf, i), x509);
return bytes;
}
-
- private static ArraySegment<byte> RentDynamicBuffer<THandle>(NegativeSizeReadMethod<THandle> method, THandle handle)
- {
- int negativeSize = method(handle, null, 0);
-
- if (negativeSize > 0)
- {
- throw Interop.Crypto.CreateOpenSslCryptographicException();
- }
-
- int targetSize = -negativeSize;
- byte[] bytes = ArrayPool<byte>.Shared.Rent(targetSize);
-
- int ret = method(handle, bytes, targetSize);
-
- if (ret != 1)
- {
- ArrayPool<byte>.Shared.Return(bytes);
- throw Interop.Crypto.CreateOpenSslCryptographicException();
- }
-
- return new ArraySegment<byte>(bytes, 0, targetSize);
- }
}
}
// See the LICENSE file in the project root for more information.
using System;
-using System.Buffers;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Security.Cryptography;
internal static partial class Interop
{
throw CreateOpenSslCryptographicException();
}
- byte[] data = ArrayPool<byte>.Shared.Rent(size);
+ byte[] data = CryptoPool.Rent(size);
int size2 = encode(handle, data);
if (size2 < 1)
Debug.Fail(
$"{nameof(OpenSslEncode)}: {nameof(getSize)} succeeded ({size}) and {nameof(encode)} failed ({size2})");
- // Since we don't know what was written, assume it was secret and clear the value.
+ // Since we don't know what was written, assume it was secret and have the
+ // CryptoPool.Return clear the whole array.
// (It doesn't matter much, since we're behind Debug.Fail)
- ArrayPool<byte>.Shared.Return(data, clearArray: true);
+ CryptoPool.Return(data);
// If it ever happens, ensure the error queue gets cleared.
// And since it didn't write the data, reporting an exception is good too.
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Security.Cryptography;
+
+internal static partial class Interop
+{
+ internal static partial class Crypto
+ {
+ internal static ArraySegment<byte> RentAsn1StringBytes(IntPtr asn1)
+ {
+ return RentDynamicBuffer((ptr, buf, i) => GetAsn1StringBytes(ptr, buf, i), asn1);
+ }
+
+ private static ArraySegment<byte> RentDynamicBuffer<THandle>(NegativeSizeReadMethod<THandle> method, THandle handle)
+ {
+ int negativeSize = method(handle, null, 0);
+
+ if (negativeSize > 0)
+ {
+ throw Interop.Crypto.CreateOpenSslCryptographicException();
+ }
+
+ int targetSize = -negativeSize;
+ byte[] bytes = CryptoPool.Rent(targetSize);
+
+ int ret = method(handle, bytes, targetSize);
+
+ if (ret != 1)
+ {
+ CryptoPool.Return(bytes);
+ throw Interop.Crypto.CreateOpenSslCryptographicException();
+ }
+
+ return new ArraySegment<byte>(bytes, 0, targetSize);
+ }
+ }
+}
memory = PeekEncodedValue();
// Guaranteed long enough
- byte[] rented = ArrayPool<byte>.Shared.Rent(memory.Length);
+ byte[] rented = CryptoPool.Rent(memory.Length);
int dataLength = 0;
try
}
finally
{
- rented.AsSpan(0, dataLength).Clear();
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, dataLength);
}
}
{
byte[] rented = null;
+ // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or
+ // BER specified offset.
+ Span<byte> tmpSpace = stackalloc byte[64];
+
ReadOnlySpan<byte> contents = GetOctetStringContents(
expectedTag,
UniversalTagNumber.GeneralizedTime,
out int bytesRead,
- ref rented);
+ ref rented,
+ tmpSpace);
DateTimeOffset value = ParseGeneralizedTime(RuleSet, contents, disallowFractions);
if (rented != null)
{
- Array.Clear(rented, 0, contents.Length);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, contents.Length);
}
_data = _data.Slice(bytesRead);
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
using System.Diagnostics;
using System.Numerics;
GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength);
// TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing
- byte[] tmp = ArrayPool<byte>.Shared.Rent(contents.Length);
+ byte[] tmp = CryptoPool.Rent(contents.Length);
BigInteger value;
try
}
finally
{
- // Clear the whole tmp so that not even the sign bit is returned to the array pool.
- Array.Clear(tmp, 0, tmp.Length);
- ArrayPool<byte>.Shared.Return(tmp);
+ // Let CryptoPool.Return clear the whole tmp so that not even the sign bit
+ // is returned to the array pool.
+ CryptoPool.Return(tmp);
}
_data = _data.Slice(headerLength + contents.Length);
memory = PeekEncodedValue();
// Guaranteed long enough
- byte[] rented = ArrayPool<byte>.Shared.Rent(memory.Length);
+ byte[] rented = CryptoPool.Rent(memory.Length);
int dataLength = 0;
try
}
finally
{
- rented.AsSpan(0, dataLength).Clear();
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, dataLength);
}
}
if (tmpSpace.Length < octetStringLength)
{
- rented = ArrayPool<byte>.Shared.Rent(octetStringLength);
+ rented = CryptoPool.Rent(octetStringLength);
tmpSpace = rented;
}
// Every 8 content bytes turns into 7 integer bytes, so scale the count appropriately.
// Add one while we're shrunk to account for the needed padding byte or the len%8 discarded bytes.
int bytesRequired = ((bytesRead / ContentByteCount) + 1) * SemanticByteCount;
- byte[] tmpBytes = ArrayPool<byte>.Shared.Rent(bytesRequired);
+ byte[] tmpBytes = CryptoPool.Rent(bytesRequired);
// Ensure all the bytes are zeroed out for BigInteger's parsing.
Array.Clear(tmpBytes, 0, tmpBytes.Length);
largeValue = new BigInteger(tmpBytes);
smallValue = null;
- Array.Clear(tmpBytes, 0, bytesWritten);
- ArrayPool<byte>.Shared.Return(tmpBytes);
+ CryptoPool.Return(tmpBytes, bytesWritten);
}
private string ReadObjectIdentifierAsString(Asn1Tag expectedTag, out int totalBytesRead)
{
if (rented != null)
{
- Array.Clear(rented, 0, contents.Length);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, contents.Length);
}
}
}
{
if (rented != null)
{
- Array.Clear(rented, 0, contents.Length);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, contents.Length);
}
}
}
if (rented != null)
{
Debug.Fail($"UtcTime did not fit in tmpSpace ({contents.Length} total)");
- Array.Clear(rented, 0, contents.Length);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, contents.Length);
}
_data = _data.Slice(bytesRead);
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Buffers;
using System.Diagnostics;
namespace System.Security.Cryptography.Asn1
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
- // If 3 bits are "unused" then build a mask for them to check for 0.
- // 1 << 3 => 0b0000_1000
- // subtract 1 => 0b000_0111
- int mask = (1 << unusedBitCount) - 1;
byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1];
- if ((lastByte & mask) != 0)
+ // T-REC-X.690-201508 sec 11.2
+ //
+ // This could be ignored for BER, but since DER is more common and
+ // it likely suggests a program error on the caller, leave it enabled for
+ // BER for now.
+ if (!CheckValidLastByte(lastByte, unusedBitCount))
{
- // T-REC-X.690-201508 sec 11.2
- //
- // This could be ignored for BER, but since DER is more common and
- // it likely suggests a program error on the caller, leave it enabled for
- // BER for now.
// TODO: Probably warrants a distinct message.
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
_offset += bitString.Length;
}
- // T-REC-X.690-201508 sec 9.2, 8.6
- private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan<byte> payload, int unusedBitCount)
+#if netcoreapp || uap || NETCOREAPP
+ /// <summary>
+ /// Write a Bit String value via a callback, with a tag UNIVERSAL 3.
+ /// </summary>
+ /// <param name="byteLength">The total number of bytes to write.</param>
+ /// <param name="state">A state object to pass to <paramref name="action"/>.</param>
+ /// <param name="action">A callback to invoke for populating the Bit String.</param>
+ /// <param name="unusedBitCount">
+ /// The number of trailing bits which are not semantic.
+ /// </param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="byteLength"/> is negative --OR--
+ /// <paramref name="unusedBitCount"/> is not in the range [0,7]
+ /// </exception>
+ /// <exception cref="CryptographicException">
+ /// <paramref name="byteLength"/> is 0 and <paramref name="unusedBitCount"/> is not 0 --OR--
+ /// <paramref name="byteLength"/> is not 0 and any of the bits identified by
+ /// <paramref name="unusedBitCount"/> is set
+ /// </exception>
+ /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
+ public void WriteBitString<TState>(
+ int byteLength,
+ TState state,
+ SpanAction<byte, TState> action,
+ int unusedBitCount = 0)
+ {
+ WriteBitStringCore(Asn1Tag.PrimitiveBitString, byteLength, state, action, unusedBitCount);
+ }
+
+ /// <summary>
+ /// Write a Bit String value via a callback, with a specified tag.
+ /// </summary>
+ /// <param name="tag">The tag to write.</param>
+ /// <param name="byteLength">The total number of bytes to write.</param>
+ /// <param name="state">A state object to pass to <paramref name="action"/>.</param>
+ /// <param name="action">A callback to invoke for populating the Bit String.</param>
+ /// <param name="unusedBitCount">
+ /// The number of trailing bits which are not semantic.
+ /// </param>
+ /// <exception cref="ArgumentException">
+ /// <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
+ /// <see cref="TagClass.Universal"/>, but
+ /// <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
+ /// the method
+ /// </exception>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="byteLength"/> is negative --OR--
+ /// <paramref name="unusedBitCount"/> is not in the range [0,7]
+ /// </exception>
+ /// <exception cref="CryptographicException">
+ /// <paramref name="byteLength"/> is 0 and <paramref name="unusedBitCount"/> is not 0 --OR--
+ /// <paramref name="byteLength"/> is not 0 and any of the bits identified by
+ /// <paramref name="unusedBitCount"/> is set
+ /// </exception>
+ /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
+ public void WriteBitString<TState>(
+ Asn1Tag tag,
+ int byteLength,
+ TState state,
+ SpanAction<byte, TState> action,
+ int unusedBitCount = 0)
+ {
+ CheckUniversalTag(tag, UniversalTagNumber.BitString);
+
+ // Primitive or constructed, doesn't matter.
+ WriteBitStringCore(tag, byteLength, state, action, unusedBitCount);
+ }
+
+ // T-REC-X.690-201508 sec 8.6
+ private void WriteBitStringCore<TState>(
+ Asn1Tag tag,
+ int byteLength,
+ TState state,
+ SpanAction<byte, TState> action,
+ int unusedBitCount = 0)
+ {
+ if (byteLength == 0)
+ {
+ WriteBitStringCore(tag, ReadOnlySpan<byte>.Empty, unusedBitCount);
+ return;
+ }
+
+ // T-REC-X.690-201508 sec 8.6.2.2
+ if (unusedBitCount < 0 || unusedBitCount > 7)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(unusedBitCount),
+ unusedBitCount,
+ SR.Cryptography_Asn_UnusedBitCountRange);
+ }
+
+ CheckDisposed();
+
+ int savedOffset = _offset;
+ Span<byte> scratchSpace;
+ byte[] ensureNoExtraCopy = null;
+ int expectedSize = 0;
+
+ // T-REC-X.690-201508 sec 9.2
+ //
+ // If it's not within a primitive segment, use the constructed encoding.
+ // (>= instead of > because of the unused bit count byte)
+ bool segmentedWrite =
+ RuleSet == AsnEncodingRules.CER && byteLength >= AsnReader.MaxCERSegmentSize;
+
+ if (segmentedWrite)
+ {
+ // Rather than call the callback multiple times, grow the buffer to allow
+ // for enough space for the final output, then return a buffer where the last segment
+ // is in the correct place. (Data will shift backwards to the right spot while writing
+ // other segments).
+ expectedSize = DetermineCerBitStringTotalLength(tag, byteLength);
+ EnsureWriteCapacity(expectedSize);
+ int overhead = expectedSize - byteLength;
+
+ // Start writing where the last content byte is in the correct place, which is
+ // after all of the overhead, but ending before the two byte end-of-contents marker.
+ int scratchStart = overhead - 2;
+ ensureNoExtraCopy = _buffer;
+ scratchSpace = _buffer.AsSpan(scratchStart, byteLength);
+
+ // Don't let gapped-writes be unpredictable.
+ scratchSpace.Clear();
+ }
+ else
+ {
+ WriteTag(tag.AsPrimitive());
+ // The unused bits byte requires +1.
+ WriteLength(byteLength + 1);
+
+ _buffer[_offset] = (byte)unusedBitCount;
+ _offset++;
+
+ scratchSpace = _buffer.AsSpan(_offset, byteLength);
+ }
+
+ action(scratchSpace, state);
+
+ // T-REC-X.690-201508 sec 11.2
+ //
+ // This could be ignored for BER, but since DER is more common and
+ // it likely suggests a program error on the caller, leave it enabled for
+ // BER for now.
+ if (!CheckValidLastByte(scratchSpace[byteLength - 1], unusedBitCount))
+ {
+ // Since we are restoring _offset we won't clear this on a grow or Dispose,
+ // so clear it now.
+ _offset = savedOffset;
+ scratchSpace.Clear();
+
+ // TODO: Probably warrants a distinct message.
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ if (segmentedWrite)
+ {
+ WriteConstructedCerBitString(tag, scratchSpace, unusedBitCount);
+ Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}");
+ Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during while writing a bit string via callback");
+ }
+ else
+ {
+ _offset += byteLength;
+ }
+ }
+#endif
+
+ private static bool CheckValidLastByte(byte lastByte, int unusedBitCount)
+ {
+ // If 3 bits are "unused" then build a mask for them to check for 0.
+ // 1 << 3 => 0b0000_1000
+ // subtract 1 => 0b000_0111
+ int mask = (1 << unusedBitCount) - 1;
+ return ((lastByte & mask) == 0);
+ }
+
+ private static int DetermineCerBitStringTotalLength(Asn1Tag tag, int contentLength)
{
const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize;
// Every segment has an "unused bit count" byte.
const int MaxCERContentSize = MaxCERSegmentSize - 1;
- Debug.Assert(payload.Length > MaxCERContentSize);
+ Debug.Assert(contentLength > MaxCERContentSize);
- WriteTag(tag.AsConstructed());
- // T-REC-X.690-201508 sec 9.1
- // Constructed CER uses the indefinite form.
- WriteLength(-1);
-
- int fullSegments = Math.DivRem(payload.Length, MaxCERContentSize, out int lastContentSize);
+ int fullSegments = Math.DivRem(contentLength, MaxCERContentSize, out int lastContentSize);
// The tag size is 1 byte.
// The length will always be encoded as 82 03 E8 (3 bytes)
// Reduce the number of copies by pre-calculating the size.
// +2 for End-Of-Contents
- int expectedSize = fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 2;
+ // +1 for 0x80 indefinite length
+ // +tag length
+ return fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 3 + tag.CalculateEncodedSize();
+ }
+
+ // T-REC-X.690-201508 sec 9.2, 8.6
+ private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan<byte> payload, int unusedBitCount)
+ {
+ const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize;
+ // Every segment has an "unused bit count" byte.
+ const int MaxCERContentSize = MaxCERSegmentSize - 1;
+ Debug.Assert(payload.Length > MaxCERContentSize);
+
+ int expectedSize = DetermineCerBitStringTotalLength(tag, payload.Length);
EnsureWriteCapacity(expectedSize);
+ int savedOffset = _offset;
+
+ WriteTag(tag.AsConstructed());
+ // T-REC-X.690-201508 sec 9.1
+ // Constructed CER uses the indefinite form.
+ WriteLength(-1);
byte[] ensureNoExtraCopy = _buffer;
- int savedOffset = _offset;
ReadOnlySpan<byte> remainingData = payload;
Span<byte> dest;
// The worst case is "1.1.1.1.1", which takes 4 bytes (5 components, with the first two condensed)
// Longer numbers get smaller: "2.1.127" is only 2 bytes. (81d (0x51) and 127 (0x7F))
// So length / 2 should prevent any reallocations.
- var localPool = ArrayPool<byte>.Shared;
- byte[] tmp = localPool.Rent(oidValue.Length / 2);
+ byte[] tmp = CryptoPool.Rent(oidValue.Length / 2);
int tmpOffset = 0;
try
}
finally
{
- Array.Clear(tmp, 0, tmpOffset);
- localPool.Return(tmp);
+ CryptoPool.Return(tmp, tmpOffset);
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
using System.Diagnostics;
using System.Runtime.InteropServices;
byte[] tmp;
- // TODO: Split this for netstandard vs netcoreapp for span?.
- var localPool = ArrayPool<byte>.Shared;
unsafe
{
fixed (char* strPtr = &MemoryMarshal.GetReference(str))
{
- tmp = localPool.Rent(size);
+ tmp = CryptoPool.Rent(size);
fixed (byte* destPtr = tmp)
{
}
WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size));
- Array.Clear(tmp, 0, size);
- localPool.Return(tmp);
+ CryptoPool.Return(tmp, size);
}
}
}
{
Array.Clear(_buffer, 0, _offset);
#if !CHECK_ACCURATE_ENSURE
- ArrayPool<byte>.Shared.Return(_buffer);
+ // clearSize: 0 because it was already cleared.
+ CryptoPool.Return(_buffer, clearSize: 0);
#endif
_buffer = null;
}
return new ReadOnlySpan<byte>(_buffer, 0, _offset);
}
+ /// <summary>
+ /// Determines if <see cref="Encode"/> would produce an output identical to
+ /// <paramref name="other"/>.
+ /// </summary>
+ /// <returns>
+ /// <see langword="true"/> if the pending encoded data is identical to <paramref name="other"/>,
+ /// <see langword="false"/> otherwise.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">
+ /// A <see cref="PushSequence()"/> or <see cref="PushSetOf()"/> has not been closed via
+ /// <see cref="PopSequence()"/> or <see cref="PopSetOf()"/>.
+ /// </exception>
+ /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
+ public bool ValueEquals(ReadOnlySpan<byte> other)
+ {
+ return EncodeAsSpan().SequenceEqual(other);
+ }
+
private void CheckDisposed()
{
if (_offset < 0)
// While the ArrayPool may have similar logic, make sure we don't run into a lot of
// "grow a little" by asking in 1k steps.
int blocks = checked(_offset + pendingCount + (BlockSize - 1)) / BlockSize;
- var localPool = ArrayPool<byte>.Shared;
- byte[] newBytes = localPool.Rent(BlockSize * blocks);
+ byte[] oldBytes = _buffer;
+ _buffer = CryptoPool.Rent(BlockSize * blocks);
- if (_buffer != null)
+ if (oldBytes != null)
{
- Buffer.BlockCopy(_buffer, 0, newBytes, 0, _offset);
- Array.Clear(_buffer, 0, _offset);
- localPool.Return(_buffer);
+ Buffer.BlockCopy(oldBytes, 0, _buffer, 0, _offset);
+ CryptoPool.Return(oldBytes, _offset);
}
#endif
#if DEBUG
// Ensure no "implicit 0" is happening
- for (int i = _offset; i < newBytes.Length; i++)
+ for (int i = _offset; i < _buffer.Length; i++)
{
- newBytes[i] ^= 0xFF;
+ _buffer[i] ^= 0xFF;
}
#endif
-
- _buffer = newBytes;
}
}
var comparer = new ArrayIndexSetOfValueComparer(buffer);
positions.Sort(comparer);
- ArrayPool<byte> localPool = ArrayPool<byte>.Shared;
- byte[] tmp = localPool.Rent(len);
+ byte[] tmp = CryptoPool.Rent(len);
pos = 0;
Debug.Assert(pos == len);
Buffer.BlockCopy(tmp, 0, buffer, start, len);
- Array.Clear(tmp, 0, len);
- localPool.Return(tmp);
+ CryptoPool.Return(tmp, len);
}
internal static void Reverse(Span<byte> span)
finally
{
CryptographicOperations.ZeroMemory(decryptedSpan);
- ArrayPool<byte>.Shared.Return(decrypted.Array);
+ CryptoPool.Return(decrypted.Array);
}
}
}
finally
{
CryptographicOperations.ZeroMemory(decryptedSpan);
- ArrayPool<byte>.Shared.Return(decrypted.Array);
+ CryptoPool.Return(decrypted.Array, clearSize: 0);
}
}
}
// * secp521r1 needs ~300 bytes (named) or ~730 (explicit)
// * KeySize (bits) should avoid re-rent for named, and probably
// gets one re-rent for explicit.
- byte[] rented = ArrayPool<byte>.Shared.Rent(key.KeySize);
+ byte[] rented = CryptoPool.Rent(key.KeySize);
int rentWritten = 0;
// If we use 6 bits from each byte, that's 22 * 6 = 132
out rentWritten))
{
int size = rented.Length;
- ArrayPool<byte>.Shared.Return(rented);
- rented = ArrayPool<byte>.Shared.Rent(checked(size * 2));
+ byte[] current = rented;
+ rented = CryptoPool.Rent(checked(size * 2));
+ CryptoPool.Return(current, rentWritten);
}
return KeyFormatHelper.ReencryptPkcs8(
finally
{
randomString.Clear();
- CryptographicOperations.ZeroMemory(rented.AsSpan(0, rentWritten));
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, rentWritten);
}
}
{
Debug.Assert(pbeParameters != null);
- byte[] rented = ArrayPool<byte>.Shared.Rent(key.KeySize);
+ byte[] rented = CryptoPool.Rent(key.KeySize);
int rentWritten = 0;
try
out rentWritten))
{
int size = rented.Length;
- ArrayPool<byte>.Shared.Return(rented);
- rented = ArrayPool<byte>.Shared.Rent(checked(size * 2));
+ byte[] current = rented;
+ rented = CryptoPool.Rent(checked(size * 2));
+ CryptoPool.Return(current, rentWritten);
}
return KeyFormatHelper.ReencryptPkcs8(
}
finally
{
- CryptographicOperations.ZeroMemory(rented.AsSpan(0, rentWritten));
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, rentWritten);
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Diagnostics;
+
+namespace System.Security.Cryptography
+{
+ internal static class CryptoPool
+ {
+ internal const int ClearAll = -1;
+
+ internal static byte[] Rent(int minimumLength) => ArrayPool<byte>.Shared.Rent(minimumLength);
+
+ internal static void Return(byte[] array, int clearSize = ClearAll)
+ {
+ Debug.Assert(clearSize <= array.Length);
+ bool clearWholeArray = clearSize < 0;
+
+ if (!clearWholeArray && clearSize != 0)
+ {
+#if netcoreapp || uap || NETCOREAPP
+ CryptographicOperations.ZeroMemory(array.AsSpan(0, clearSize));
+#else
+ Array.Clear(array, 0, clearSize);
+#endif
+ }
+
+ ArrayPool<byte>.Shared.Return(array, clearWholeArray);
+ }
+ }
+}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
+using System.Diagnostics;
using Internal.Cryptography;
using Microsoft.Win32.SafeHandles;
using static Interop.BCrypt;
#endif
public sealed partial class DSACng : DSA
{
+ // As of FIPS 186-4 the maximum Q size is 32 bytes.
+ //
+ // See also: cbGroupSize at
+ // https://docs.microsoft.com/en-us/windows/desktop/api/bcrypt/ns-bcrypt-_bcrypt_dsa_key_blob_v2
+ private const int WindowsMaxQSize = 32;
+
public override byte[] CreateSignature(byte[] rgbHash)
{
if (rgbHash == null)
throw new ArgumentNullException(nameof(rgbHash));
}
- ReadOnlySpan<byte> source = rgbHash;
- byte[] arrayToReturnToArrayPool = AdjustHashSizeIfNecessaryWithArrayPool(ref source);
- try
- {
- using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
- {
- unsafe
- {
- return CngCommon.SignHash(keyHandle, source, AsymmetricPaddingMode.None, null, source.Length * 2);
- }
- }
- }
- finally
+ Span<byte> stackBuf = stackalloc byte[WindowsMaxQSize];
+ ReadOnlySpan<byte> source = AdjustHashSizeIfNecessary(rgbHash, stackBuf);
+
+ using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
{
- if (arrayToReturnToArrayPool != null)
+ unsafe
{
- Array.Clear(arrayToReturnToArrayPool, 0, source.Length);
- ArrayPool<byte>.Shared.Return(arrayToReturnToArrayPool);
+ return CngCommon.SignHash(keyHandle, source, AsymmetricPaddingMode.None, null, source.Length * 2);
}
}
}
public override unsafe bool TryCreateSignature(ReadOnlySpan<byte> hash, Span<byte> destination, out int bytesWritten)
{
- byte[] arrayToReturnToArrayPool = AdjustHashSizeIfNecessaryWithArrayPool(ref hash);
- try
+ Span<byte> stackBuf = stackalloc byte[WindowsMaxQSize];
+ ReadOnlySpan<byte> source = AdjustHashSizeIfNecessary(hash, stackBuf);
+
+ using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
{
- using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
- {
- return CngCommon.TrySignHash(keyHandle, hash, destination, AsymmetricPaddingMode.None, null, out bytesWritten);
- }
- }
- finally
- {
- if (arrayToReturnToArrayPool != null)
- {
- Array.Clear(arrayToReturnToArrayPool, 0, hash.Length);
- ArrayPool<byte>.Shared.Return(arrayToReturnToArrayPool);
- }
+ return CngCommon.TrySignHash(keyHandle, source, destination, AsymmetricPaddingMode.None, null, out bytesWritten);
}
}
public override bool VerifySignature(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
{
- byte[] arrayToReturnToArrayPool = AdjustHashSizeIfNecessaryWithArrayPool(ref hash);
- try
- {
- using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
- {
- unsafe
- {
- return CngCommon.VerifyHash(keyHandle, hash, signature, AsymmetricPaddingMode.None, null);
- }
- }
- }
- finally
+ Span<byte> stackBuf = stackalloc byte[WindowsMaxQSize];
+ ReadOnlySpan<byte> source = AdjustHashSizeIfNecessary(hash, stackBuf);
+
+ using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
{
- if (arrayToReturnToArrayPool != null)
+ unsafe
{
- Array.Clear(arrayToReturnToArrayPool, 0, hash.Length);
- ArrayPool<byte>.Shared.Return(arrayToReturnToArrayPool);
+ return CngCommon.VerifyHash(keyHandle, source, signature, AsymmetricPaddingMode.None, null);
}
}
}
- private byte[] AdjustHashSizeIfNecessaryWithArrayPool(ref ReadOnlySpan<byte> hash)
+ private ReadOnlySpan<byte> AdjustHashSizeIfNecessary(ReadOnlySpan<byte> hash, Span<byte> stackBuf)
{
+ Debug.Assert(stackBuf.Length == WindowsMaxQSize);
+
// Windows CNG requires that the hash output and q match sizes, but we can better
// interoperate with other FIPS 186-3 implementations if we perform truncation
// here, before sending it to CNG. Since this is a scenario presented in the
// presented in the CAVP reference test suite, we can confirm our implementation.
int qLength = ComputeQLength();
+ Debug.Assert(qLength <= WindowsMaxQSize);
if (qLength == hash.Length)
{
- return null;
+ return hash;
}
- else if (qLength < hash.Length)
- {
- hash = hash.Slice(0, qLength);
- return null;
- }
- else
+
+ if (qLength < hash.Length)
{
- byte[] arrayPoolPaddedHash = ArrayPool<byte>.Shared.Rent(qLength);
- hash.CopyTo(new Span<byte>(arrayPoolPaddedHash, qLength - hash.Length, hash.Length));
- hash = new ReadOnlySpan<byte>(arrayPoolPaddedHash, 0, qLength);
- return arrayPoolPaddedHash;
+ return hash.Slice(0, qLength);
}
+
+ int zeroByteCount = qLength - hash.Length;
+ stackBuf.Slice(0, zeroByteCount).Clear();
+ hash.CopyTo(stackBuf.Slice(zeroByteCount));
+ return stackBuf.Slice(0, qLength);
}
private int ComputeQLength()
SafeDsaHandle key = _key.Value;
int signatureSize = Interop.Crypto.DsaEncodedSignatureSize(key);
- byte[] signature = ArrayPool<byte>.Shared.Rent(signatureSize);
+ byte[] signature = CryptoPool.Rent(signatureSize);
try
{
bool success = Interop.Crypto.DsaSign(key, rgbHash, new Span<byte>(signature, 0, signatureSize), out signatureSize);
}
finally
{
- Array.Clear(signature, 0, signatureSize);
- ArrayPool<byte>.Shared.Return(signature);
+ CryptoPool.Return(signature, signatureSize);
}
}
byte[] converted;
SafeDsaHandle key = _key.Value;
int signatureSize = Interop.Crypto.DsaEncodedSignatureSize(key);
- byte[] signature = ArrayPool<byte>.Shared.Rent(signatureSize);
+ byte[] signature = CryptoPool.Rent(signatureSize);
try
{
bool success = Interop.Crypto.DsaSign(key, hash, new Span<byte>(signature, 0, signatureSize), out signatureSize);
}
finally
{
- Array.Clear(signature, 0, signatureSize);
- ArrayPool<byte>.Shared.Return(signature);
+ CryptoPool.Return(signature, signatureSize);
}
if (converted.Length <= destination.Length)
if (secretLength > StackAllocMax)
{
- rented = ArrayPool<byte>.Shared.Rent(secretLength);
+ rented = CryptoPool.Rent(secretLength);
secret = new Span<byte>(rented, 0, secretLength);
}
else
if (rented != null)
{
- Array.Clear(rented, 0, secretLength);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, secretLength);
}
}
}
byte[] converted;
int signatureLength = Interop.Crypto.EcDsaSize(key);
- byte[] signature = ArrayPool<byte>.Shared.Rent(signatureLength);
+ byte[] signature = CryptoPool.Rent(signatureLength);
try
{
if (!Interop.Crypto.EcDsaSign(hash, new Span<byte>(signature, 0, signatureLength), ref signatureLength, key))
}
finally
{
- Array.Clear(signature, 0, signatureLength);
- ArrayPool<byte>.Shared.Return(signature);
+ CryptoPool.Return(signature, signatureLength);
}
if (converted.Length <= destination.Length)
if (keyParameters != null && algId.Parameters != null)
{
ReadOnlySpan<byte> algIdParameters = algId.Parameters.Value.Span;
- byte[] verify = ArrayPool<byte>.Shared.Rent(algIdParameters.Length);
- try
+ // X.509 SubjectPublicKeyInfo specifies DER encoding.
+ // RFC 5915 specifies DER encoding for EC Private Keys.
+ // So we can compare as DER.
+ using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
{
- // X.509 SubjectPublicKeyInfo specifies DER encoding.
- // RFC 5915 specifies DER encoding for EC Private Keys.
- // So we can compare as DER.
- using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
+ keyParameters.Value.Encode(writer);
+
+ if (!writer.ValueEquals(algIdParameters))
{
- keyParameters.Value.Encode(writer);
- if (!writer.TryEncode(verify, out int written) ||
- written != algIdParameters.Length ||
- !algIdParameters.SequenceEqual(new ReadOnlySpan<byte>(verify, 0, written)))
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
}
- finally
- {
- // verify contains public information and does not need to be cleared.
- ArrayPool<byte>.Shared.Return(verify);
- }
}
}
private static void WriteUncompressedPublicKey(in ECParameters ecParameters, AsnWriter writer)
{
int publicKeyLength = ecParameters.Q.X.Length * 2 + 1;
- Span<byte> publicKeyBytes = stackalloc byte[0];
- byte[] publicKeyRented = null;
-
- if (publicKeyLength < 256)
- {
- publicKeyBytes = stackalloc byte[publicKeyLength];
- }
- else
- {
- publicKeyRented = ArrayPool<byte>.Shared.Rent(publicKeyLength);
- publicKeyBytes = publicKeyRented.AsSpan(0, publicKeyLength);
- }
- try
- {
- publicKeyBytes[0] = 0x04;
- ecParameters.Q.X.AsSpan().CopyTo(publicKeyBytes.Slice(1));
- ecParameters.Q.Y.AsSpan().CopyTo(publicKeyBytes.Slice(1 + ecParameters.Q.X.Length));
-
- writer.WriteBitString(publicKeyBytes);
- }
- finally
- {
- CryptographicOperations.ZeroMemory(publicKeyBytes);
-
- if (publicKeyRented != null)
+ writer.WriteBitString(
+ publicKeyLength,
+ ecParameters,
+ (publicKeyBytes, ecParams) =>
{
- ArrayPool<byte>.Shared.Return(publicKeyRented);
- }
- }
+ publicKeyBytes[0] = 0x04;
+ ecParams.Q.X.AsSpan().CopyTo(publicKeyBytes.Slice(1));
+ ecParams.Q.Y.AsSpan().CopyTo(publicKeyBytes.Slice(1 + ecParams.Q.X.Length));
+ });
}
internal static AsnWriter WriteECPrivateKey(in ECParameters ecParameters)
// No supported encryption algorithms produce more bytes of decryption output than there
// were of decryption input.
- byte[] decrypted = ArrayPool<byte>.Shared.Rent(epki.EncryptedData.Length);
+ byte[] decrypted = CryptoPool.Rent(epki.EncryptedData.Length);
Memory<byte> decryptedMemory = decrypted;
try
finally
{
CryptographicOperations.ZeroMemory(decryptedMemory.Span);
- ArrayPool<byte>.Shared.Return(decrypted);
+ CryptoPool.Return(decrypted, clearSize: 0);
}
}
Span<byte> salt = stackalloc byte[16];
// We need at least one block size beyond the input data size.
- encryptedRent = ArrayPool<byte>.Shared.Rent(
+ encryptedRent = CryptoPool.Rent(
checked(pkcs8Span.Length + (cipher.BlockSize / 8)));
RandomNumberGenerator.Fill(salt);
finally
{
CryptographicOperations.ZeroMemory(encryptedSpan);
- ArrayPool<byte>.Shared.Return(encryptedRent);
+ CryptoPool.Return(encryptedRent, clearSize: 0);
writer?.Dispose();
cipher.Dispose();
// No supported encryption algorithms produce more bytes of decryption output than there
// were of decryption input.
- byte[] decrypted = ArrayPool<byte>.Shared.Rent(epki.EncryptedData.Length);
+ byte[] decrypted = CryptoPool.Rent(epki.EncryptedData.Length);
try
{
}
catch (CryptographicException e)
{
- ArrayPool<byte>.Shared.Return(decrypted);
+ CryptoPool.Return(decrypted);
throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
}
}
finally
{
CryptographicOperations.ZeroMemory(decrypted);
- ArrayPool<byte>.Shared.Return(decrypted.Array);
+ CryptoPool.Return(decrypted.Array, clearSize: 0);
}
}
finally
{
CryptographicOperations.ZeroMemory(decrypted);
- ArrayPool<byte>.Shared.Return(decrypted.Array);
+ CryptoPool.Return(decrypted.Array, clearSize: 0);
}
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
{
internal const int IterationLimit = 600000;
- private static ArrayPool<byte> ArrayPool => ArrayPool<byte>.Shared;
-
private static CryptographicException AlgorithmKdfRequiresChars(string algId)
{
return new CryptographicException(SR.Cryptography_AlgKdfRequiresChars, algId);
if (byteCount > buf.Length)
{
- rented = ArrayPool.Rent(byteCount);
+ rented = CryptoPool.Rent(byteCount);
buf = rented.AsSpan(0, byteCount);
}
else
if (rented != null)
{
- ArrayPool.Return(rented);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
}
byte[] derivedKey;
byte[] iv = cipher.IV;
- byte[] sourceRent = ArrayPool.Rent(source.Length);
+ byte[] sourceRent = CryptoPool.Rent(source.Length);
int keySizeBytes = cipher.KeySize / 8;
int iterationCount = pbeParameters.IterationCount;
HashAlgorithmName prf = pbeParameters.HashAlgorithm;
}
finally
{
- CryptographicOperations.ZeroMemory(sourceRent.AsSpan(0, source.Length));
- ArrayPool.Return(sourceRent);
+ CryptoPool.Return(sourceRent, source.Length);
}
}
}
if (byteCount > buf.Length)
{
- rented = ArrayPool.Rent(byteCount);
+ rented = CryptoPool.Rent(byteCount);
buf = rented.AsSpan(0, byteCount);
}
else
effectivePasswordBytes = buf;
}
- return Pbes2Decrypt(
- algorithmParameters,
- effectivePasswordBytes,
- encryptedData,
- destination);
+ try
+ {
+ return Pbes2Decrypt(
+ algorithmParameters,
+ effectivePasswordBytes,
+ encryptedData,
+ destination);
+ }
+ finally
+ {
+ if (rented != null)
+ {
+ CryptoPool.Return(rented, buf.Length);
+ }
+ }
}
}
// When we define a Span-based decryption API this should be changed to use it.
byte[] tmpKey = new byte[key.Length];
byte[] tmpIv = new byte[iv.Length];
- byte[] rentedEncryptedData = ArrayPool.Rent(encryptedData.Length);
- byte[] rentedDestination = ArrayPool.Rent(destination.Length);
+ byte[] rentedEncryptedData = CryptoPool.Rent(encryptedData.Length);
+ byte[] rentedDestination = CryptoPool.Rent(destination.Length);
// Keep all the arrays pinned so they can be correctly cleared
fixed (byte* tmpKeyPtr = tmpKey)
{
CryptographicOperations.ZeroMemory(tmpKey);
CryptographicOperations.ZeroMemory(tmpIv);
- CryptographicOperations.ZeroMemory(rentedEncryptedData.AsSpan(0, encryptedData.Length));
- CryptographicOperations.ZeroMemory(rentedDestination.AsSpan(0, destination.Length));
- ArrayPool.Return(rentedEncryptedData);
- ArrayPool.Return(rentedDestination);
+ CryptoPool.Return(rentedEncryptedData, encryptedData.Length);
+ CryptoPool.Return(rentedDestination, destination.Length);
}
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
}
else
{
- IRented = ArrayPool<byte>.Shared.Rent(ILen);
+ IRented = CryptoPool.Rent(ILen);
I = IRented.AsSpan(0, ILen);
}
if (IRented != null)
{
- ArrayPool<byte>.Shared.Return(IRented);
+ CryptoPool.Return(IRented, clearSize: 0);
}
hash.Dispose();
{
if (encrypt && data.Length == 0)
{
- byte[] rented = ArrayPool<byte>.Shared.Rent(modulusSizeInBytes);
+ byte[] rented = CryptoPool.Rent(modulusSizeInBytes);
Span<byte> paddedMessage = new Span<byte>(rented, 0, modulusSizeInBytes);
try
finally
{
CryptographicOperations.ZeroMemory(paddedMessage);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
{
if (encrypt && data.Length == 0)
{
- byte[] rented = ArrayPool<byte>.Shared.Rent(modulusSizeInBytes);
+ byte[] rented = CryptoPool.Rent(modulusSizeInBytes);
Span<byte> paddedMessage = new Span<byte>(rented, 0, modulusSizeInBytes);
try
finally
{
CryptographicOperations.ZeroMemory(paddedMessage);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
try
{
- buf = ArrayPool<byte>.Shared.Rent(rsaSize);
+ buf = CryptoPool.Rent(rsaSize);
destination = new Span<byte>(buf, 0, rsaSize);
if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten))
finally
{
CryptographicOperations.ZeroMemory(destination);
- ArrayPool<byte>.Shared.Return(buf);
+ CryptoPool.Return(buf, clearSize: 0);
}
}
if (rsaPaddingProcessor != null)
{
- paddingBuf = ArrayPool<byte>.Shared.Rent(rsaSize);
+ paddingBuf = CryptoPool.Rent(rsaSize);
decryptBuf = paddingBuf;
}
// DecryptBuf is paddingBuf if paddingBuf is not null, erase it before returning it.
// If paddingBuf IS null then decryptBuf was destination, and shouldn't be cleared.
CryptographicOperations.ZeroMemory(decryptBuf);
- ArrayPool<byte>.Shared.Return(paddingBuf);
+ CryptoPool.Return(paddingBuf, clearSize: 0);
}
}
}
if (rsaPaddingProcessor != null)
{
Debug.Assert(rsaPadding == Interop.Crypto.RsaPadding.NoPadding);
- byte[] rented = ArrayPool<byte>.Shared.Rent(rsaSize);
+ byte[] rented = CryptoPool.Rent(rsaSize);
Span<byte> tmp = new Span<byte>(rented, 0, rsaSize);
try
finally
{
CryptographicOperations.ZeroMemory(tmp);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
else
return false;
}
- byte[] pssRented = ArrayPool<byte>.Shared.Rent(bytesRequired);
+ byte[] pssRented = CryptoPool.Rent(bytesRequired);
Span<byte> pssBytes = new Span<byte>(pssRented, 0, bytesRequired);
processor.EncodePss(hash, pssBytes, KeySize);
int ret = Interop.Crypto.RsaSignPrimitive(pssBytes, destination, rsa);
- pssBytes.Clear();
- ArrayPool<byte>.Shared.Return(pssRented);
+ CryptoPool.Return(pssRented, bytesRequired);
CheckReturn(ret);
return false;
}
- byte[] rented = ArrayPool<byte>.Shared.Rent(requiredBytes);
+ byte[] rented = CryptoPool.Rent(requiredBytes);
Span<byte> unwrapped = new Span<byte>(rented, 0, requiredBytes);
try
}
finally
{
- unwrapped.Clear();
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, requiredBytes);
}
}
throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
}
- byte[] rented = ArrayPool<byte>.Shared.Rent(rsaSize);
+ byte[] rented = CryptoPool.Rent(rsaSize);
Span<byte> tmp = new Span<byte>(rented, 0, rsaSize);
try
}
finally
{
- tmp.Clear();
- ArrayPool<byte>.Shared.Return(rented);
+ CryptographicOperations.ZeroMemory(tmp);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
}
int maxOutputSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);
- byte[] rented = ArrayPool<byte>.Shared.Rent(maxOutputSize);
- Span<byte> contentsSpan = Span<byte>.Empty;
+ byte[] rented = CryptoPool.Rent(maxOutputSize);
+ int bytesWritten = 0;
try
{
- if (!TryDecrypt(keys.PrivateKey, data, rented, padding, out int bytesWritten))
+ if (!TryDecrypt(keys.PrivateKey, data, rented, padding, out bytesWritten))
{
Debug.Fail($"TryDecrypt returned false with a modulus-sized destination");
throw new CryptographicException();
}
- contentsSpan = new Span<byte>(rented, 0, bytesWritten);
+ Span<byte> contentsSpan = new Span<byte>(rented, 0, bytesWritten);
return contentsSpan.ToArray();
}
finally
{
- CryptographicOperations.ZeroMemory(contentsSpan);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, bytesWritten);
}
}
Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Oaep);
RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm);
- byte[] rented = ArrayPool<byte>.Shared.Rent(modulusSizeInBytes);
+ byte[] rented = CryptoPool.Rent(modulusSizeInBytes);
Span<byte> unpaddedData = Span<byte>.Empty;
try
finally
{
CryptographicOperations.ZeroMemory(unpaddedData);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
return false;
}
- byte[] rented = ArrayPool<byte>.Shared.Rent(rsaSize);
+ byte[] rented = CryptoPool.Rent(rsaSize);
Span<byte> buf = new Span<byte>(rented, 0, rsaSize);
processor.EncodePss(hash, buf, keySize);
finally
{
CryptographicOperations.ZeroMemory(buf);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
return false;
}
- byte[] rented = ArrayPool<byte>.Shared.Rent(rsaSize);
+ byte[] rented = CryptoPool.Rent(rsaSize);
Span<byte> unwrapped = new Span<byte>(rented, 0, rsaSize);
try
}
finally
{
- unwrapped.Clear();
- ArrayPool<byte>.Shared.Return(rented);
+ CryptographicOperations.ZeroMemory(unwrapped);
+ CryptoPool.Return(rented, clearSize: 0);
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Diagnostics;
RandomNumberGenerator.Fill(seed);
// 2(e)
- dbMask = ArrayPool<byte>.Shared.Rent(db.Length);
+ dbMask = CryptoPool.Rent(db.Length);
dbMaskSpan = new Span<byte>(dbMask, 0, db.Length);
Mgf1(hasher, seed, dbMaskSpan);
{
if (dbMask != null)
{
- dbMaskSpan.Clear();
- ArrayPool<byte>.Shared.Return(dbMask);
+ CryptographicOperations.ZeroMemory(dbMaskSpan);
+ CryptoPool.Return(dbMask, clearSize: 0);
}
}
}
// seed = seedMask XOR maskedSeed
Xor(seed, maskedSeed);
- byte[] tmp = ArrayPool<byte>.Shared.Rent(source.Length);
+ byte[] tmp = CryptoPool.Rent(source.Length);
try
{
}
finally
{
- Array.Clear(tmp, 0, source.Length);
- ArrayPool<byte>.Shared.Return(tmp);
+ CryptoPool.Return(tmp, source.Length);
}
}
}
Span<byte> hDest = em.Slice(dbLen, _hLen);
em[emLen - 1] = 0xBC;
- byte[] dbMaskRented = ArrayPool<byte>.Shared.Rent(dbLen);
+ byte[] dbMaskRented = CryptoPool.Rent(dbLen);
Span<byte> dbMask = new Span<byte>(dbMaskRented, 0, dbLen);
using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
}
}
- dbMask.Clear();
- ArrayPool<byte>.Shared.Return(dbMaskRented);
+ CryptographicOperations.ZeroMemory(dbMask);
+ CryptoPool.Return(dbMaskRented, clearSize: 0);
}
internal bool VerifyPss(ReadOnlySpan<byte> mHash, ReadOnlySpan<byte> em, int keySize)
}
// 7. dbMask = MGF(H, emLen - hLen - 1)
- byte[] dbMaskRented = ArrayPool<byte>.Shared.Rent(maskedDb.Length);
+ byte[] dbMaskRented = CryptoPool.Rent(maskedDb.Length);
Span<byte> dbMask = new Span<byte>(dbMaskRented, 0, maskedDb.Length);
try
}
finally
{
- dbMask.Clear();
- ArrayPool<byte>.Shared.Return(dbMaskRented);
+ CryptographicOperations.ZeroMemory(dbMask);
+ CryptoPool.Return(dbMaskRented, clearSize: 0);
}
}
// OpenSSL 1.1 does not allow partial overlap.
if (input == output && inputOffset != outputOffset)
{
- byte[] tmp = ArrayPool<byte>.Shared.Rent(count);
+ byte[] tmp = CryptoPool.Rent(count);
+ int written = 0;
try
{
- int written = CipherUpdate(input, inputOffset, count, tmp, 0);
+ written = CipherUpdate(input, inputOffset, count, tmp, 0);
Buffer.BlockCopy(tmp, 0, output, outputOffset, written);
return written;
}
finally
{
- CryptographicOperations.ZeroMemory(tmp.AsSpan(0, count));
- ArrayPool<byte>.Shared.Return(tmp);
+ CryptoPool.Return(tmp, written);
}
}
<Compile Include="$(CommonPath)\System\Memory\PointerMemoryManager.cs">
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\DSAKeyFormatHelper.cs">
<Link>Common\System\Security\Cryptography\DSAKeyFormatHelper.cs</Link>
</Compile>
<ItemGroup>
<None Include="@(AsnXml)" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;
protected virtual bool TryHashData(ReadOnlySpan<byte> data, Span<byte> destination, HashAlgorithmName hashAlgorithm, out int bytesWritten)
{
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
throw HashAlgorithmNameNullOrEmpty();
}
- for (int i = 256; ; i = checked(i * 2))
+ // The biggest hash algorithm supported is SHA512, which is only 64 bytes (512 bits).
+ // So this should realistically never hit the fallback
+ // (it'd require a derived type to add support for a different hash algorithm, and that
+ // algorithm to have a large output.)
+ Span<byte> buf = stackalloc byte[128];
+ ReadOnlySpan<byte> hash = stackalloc byte[0];
+
+ if (TryHashData(data, buf, hashAlgorithm, out int hashLength))
+ {
+ hash = buf.Slice(0, hashLength);
+ }
+ else
{
- int hashLength = 0;
- byte[] hash = ArrayPool<byte>.Shared.Rent(i);
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
+ byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
- if (TryHashData(data, hash, hashAlgorithm, out hashLength))
- {
- return VerifySignature(new ReadOnlySpan<byte>(hash, 0, hashLength), signature);
- }
+ data.CopyTo(array);
+ hash = HashData(array, 0, data.Length, hashAlgorithm);
}
finally
{
- Array.Clear(hash, 0, hashLength);
- ArrayPool<byte>.Shared.Return(hash);
+ Array.Clear(array, 0, data.Length);
+ ArrayPool<byte>.Shared.Return(array);
}
}
+
+ return VerifySignature(hash, signature);
}
public virtual bool VerifySignature(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature) =>
throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
}
- for (int i = 256; ; i = checked(i * 2))
+ // The biggest hash algorithm supported is SHA512, which is only 64 bytes (512 bits).
+ // So this should realistically never hit the fallback
+ // (it'd require a derived type to add support for a different hash algorithm, and that
+ // algorithm to have a large output.)
+ Span<byte> buf = stackalloc byte[128];
+ ReadOnlySpan<byte> hash = stackalloc byte[0];
+
+ if (TryHashData(data, buf, hashAlgorithm, out int hashLength))
{
- int hashLength = 0;
- byte[] hash = ArrayPool<byte>.Shared.Rent(i);
+ hash = buf.Slice(0, hashLength);
+ }
+ else
+ {
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
+ byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
- if (TryHashData(data, hash, hashAlgorithm, out hashLength))
- {
- return VerifyHash(new ReadOnlySpan<byte>(hash, 0, hashLength), signature);
- }
+ data.CopyTo(array);
+ hash = HashData(array, 0, data.Length, hashAlgorithm);
}
finally
{
- Array.Clear(hash, 0, hashLength);
- ArrayPool<byte>.Shared.Return(hash);
+ Array.Clear(array, 0, data.Length);
+ ArrayPool<byte>.Shared.Return(array);
}
}
+
+ return VerifyHash(hash, signature);
}
public bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm)
protected virtual bool TryHashData(ReadOnlySpan<byte> data, Span<byte> destination, HashAlgorithmName hashAlgorithm, out int bytesWritten)
{
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
protected virtual bool TryHashData(ReadOnlySpan<byte> data, Span<byte> destination, HashAlgorithmName hashAlgorithm, out int bytesWritten)
{
byte[] result;
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
for (int i = 256; ; i = checked(i * 2))
{
int hashLength = 0;
- byte[] hash = ArrayPool<byte>.Shared.Rent(i);
+ byte[] hash = CryptoPool.Rent(i);
try
{
if (TryHashData(data, hash, hashAlgorithm, out hashLength))
}
finally
{
- Array.Clear(hash, 0, hashLength);
- ArrayPool<byte>.Shared.Return(hash);
+ CryptoPool.Return(hash, hashLength);
}
}
}
while (true)
{
- byte[] rented = ArrayPool<byte>.Shared.Rent(rentSize);
+ byte[] rented = CryptoPool.Rent(rentSize);
rentSize = rented.Length;
int pkcs1Size = 0;
}
finally
{
- CryptographicOperations.ZeroMemory(rented.AsSpan(0, pkcs1Size));
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, pkcs1Size);
}
}
}
while (true)
{
- byte[] rented = ArrayPool<byte>.Shared.Rent(rentSize);
+ byte[] rented = CryptoPool.Rent(rentSize);
rentSize = rented.Length;
int pkcs1Size = 0;
}
finally
{
- CryptographicOperations.ZeroMemory(rented.AsSpan(0, pkcs1Size));
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, pkcs1Size);
}
}
}
public virtual void GetBytes(Span<byte> data)
{
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
public virtual void GetNonZeroBytes(Span<byte> data)
{
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] array = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
if (password == null)
throw new NullReferenceException(); // This "should" be ArgumentNullException but for compat, we throw NullReferenceException.
- _salt = salt.CloneByteArray();
+ _salt = new byte[salt.Length + sizeof(uint)];
+ salt.AsSpan().CopyTo(_salt);
_iterations = (uint)iterations;
_password = password.CloneByteArray();
HashAlgorithm = hashAlgorithm;
if (iterations <= 0)
throw new ArgumentOutOfRangeException(nameof(iterations), SR.ArgumentOutOfRange_NeedPosNum);
- _salt = Helpers.GenerateRandom(saltSize);
+ _salt = new byte[saltSize + sizeof(uint)];
+ RandomNumberGenerator.Fill(_salt.AsSpan(0, saltSize));
+
_iterations = (uint)iterations;
_password = Encoding.UTF8.GetBytes(password);
HashAlgorithm = hashAlgorithm;
{
get
{
- return _salt.CloneByteArray();
+ return _salt.AsSpan(0, _salt.Length - sizeof(uint)).ToArray();
}
set
throw new ArgumentNullException(nameof(value));
if (value.Length < MinimumSaltSize)
throw new ArgumentException(SR.Cryptography_PasswordDerivedBytes_FewBytesSalt);
- _salt = value.CloneByteArray();
+
+ _salt = new byte[value.Length + sizeof(uint)];
+ value.AsSpan().CopyTo(_salt);
Initialize();
}
}
while (offset < cb)
{
- byte[] T_block = Func();
+ Func();
int remainder = cb - offset;
- if (remainder > _blockSize)
+ if (remainder >= _blockSize)
{
- Buffer.BlockCopy(T_block, 0, password, offset, _blockSize);
+ Buffer.BlockCopy(_buffer, 0, password, offset, _blockSize);
offset += _blockSize;
}
else
{
- Buffer.BlockCopy(T_block, 0, password, offset, remainder);
- offset += remainder;
- Buffer.BlockCopy(T_block, remainder, _buffer, _startIndex, _blockSize - remainder);
- _endIndex += (_blockSize - remainder);
+ Buffer.BlockCopy(_buffer, 0, password, offset, remainder);
+ _startIndex = remainder;
+ _endIndex = _buffer.Length;
return password;
}
}
// This function is defined as follows:
// Func (S, i) = HMAC(S || i) ^ HMAC2(S || i) ^ ... ^ HMAC(iterations) (S || i)
// where i is the block number.
- private byte[] Func()
+ private void Func()
{
- byte[] temp = new byte[_salt.Length + sizeof(uint)];
- Buffer.BlockCopy(_salt, 0, temp, 0, _salt.Length);
- Helpers.WriteInt(_block, temp, _salt.Length);
+ Helpers.WriteInt(_block, _salt, _salt.Length - sizeof(uint));
+ Debug.Assert(_blockSize == _buffer.Length);
- byte[] ui = ArrayPool<byte>.Shared.Rent(_blockSize);
- try
- {
- Span<byte> uiSpan = new Span<byte>(ui, 0, _blockSize);
+ // The biggest _blockSize we have is from SHA512, which is 64 bytes.
+ // Since we have a closed set of supported hash algorithms (OpenHmac())
+ // we can know this always fits.
+ //
+ Span<byte> uiSpan = stackalloc byte[64];
+ uiSpan = uiSpan.Slice(0, _blockSize);
- if (!_hmac.TryComputeHash(temp, uiSpan, out int bytesWritten) || bytesWritten != _blockSize)
- throw new CryptographicException();
+ if (!_hmac.TryComputeHash(_salt, uiSpan, out int bytesWritten) || bytesWritten != _blockSize)
+ {
+ throw new CryptographicException();
+ }
- byte[] ret = new byte[_blockSize];
- uiSpan.CopyTo(ret);
+ uiSpan.CopyTo(_buffer);
- for (int i = 2; i <= _iterations; i++)
+ for (int i = 2; i <= _iterations; i++)
+ {
+ if (!_hmac.TryComputeHash(uiSpan, uiSpan, out bytesWritten) || bytesWritten != _blockSize)
{
- if (!_hmac.TryComputeHash(uiSpan, uiSpan, out bytesWritten) || bytesWritten != _blockSize)
- throw new CryptographicException();
-
- for (int j = 0; j < _blockSize; j++)
- {
- ret[j] ^= ui[j];
- }
+ throw new CryptographicException();
}
- // increment the block count.
- _block++;
- return ret;
- }
- finally
- {
- Array.Clear(ui, 0, _blockSize);
- ArrayPool<byte>.Shared.Return(ui);
+ for (int j = _buffer.Length - 1; j >= 0; j--)
+ {
+ _buffer[j] ^= uiSpan[j];
+ }
}
+
+ // increment the block count.
+ _block++;
}
}
}
Assert.NotEqual(first, second);
}
- [Fact]
- public static void GetBytes_StreamLike()
+ [Theory]
+ [InlineData(2)]
+ [InlineData(5)]
+ [InlineData(10)]
+ [InlineData(16)]
+ [InlineData(20)]
+ [InlineData(25)]
+ [InlineData(32)]
+ [InlineData(40)]
+ [InlineData(192)]
+ public static void GetBytes_StreamLike(int size)
{
byte[] first;
using (var deriveBytes = new Rfc2898DeriveBytes(TestPassword, s_testSalt))
{
- first = deriveBytes.GetBytes(32);
+ first = deriveBytes.GetBytes(size);
}
byte[] second = new byte[first.Length];
Assert.Equal(first, second);
}
-
+
+ [Theory]
+ [InlineData(2)]
+ [InlineData(5)]
+ [InlineData(10)]
+ [InlineData(16)]
+ [InlineData(20)]
+ [InlineData(25)]
+ [InlineData(32)]
+ [InlineData(40)]
+ [InlineData(192)]
+ public static void GetBytes_StreamLike_OneAtATime(int size)
+ {
+ byte[] first;
+
+ using (var deriveBytes = new Rfc2898DeriveBytes(TestPasswordB, s_testSaltB))
+ {
+ first = deriveBytes.GetBytes(size);
+ }
+
+ byte[] second = new byte[first.Length];
+
+ // Reset
+ using (var deriveBytes = new Rfc2898DeriveBytes(TestPasswordB, s_testSaltB))
+ {
+ for (int i = 0; i < second.Length; i++)
+ {
+ second[i] = deriveBytes.GetBytes(1)[0];
+ }
+ }
+
+ Assert.Equal(first, second);
+ }
+
[Fact]
public static void GetBytes_KnownValues_1()
{
<Compile Include="$(CommonPath)\System\Memory\PointerMemoryManager.cs">
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\CngPkcs8.cs">
<Link>Common\System\Security\Cryptography\CngPkcs8.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Memory\PointerMemoryManager.cs">
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml">
<Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</Link>
</AsnXml>
</PropertyGroup>
<Import Project="$(CommonPath)\System\Security\Cryptography\Asn1Reader\System.Security.Cryptography.Asn1Reader.Shared.projitems" />
<ItemGroup>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="Asn1\Reader\Asn1ReaderTests.cs" />
<Compile Include="Asn1\Reader\ComprehensiveReadTests.cs" />
<Compile Include="Asn1\Reader\ParseTag.cs" />
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
<Compile Include="$(CommonPath)\System\Memory\PointerMemoryManager.cs">
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\DSAOpenSsl.cs">
<Link>Common\System\Security\Cryptography\DSAOpenSsl.cs</Link>
</Compile>
// See the LICENSE file in the project root for more information.
using System;
-using System.Buffers;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;
}
else
{
- poolBytes = ArrayPool<byte>.Shared.Rent(reader.PeekContentBytes().Length);
+ poolBytes = CryptoPool.Rent(reader.PeekContentBytes().Length);
if (!reader.TryCopyOctetStringBytes(poolBytes, out bytesWritten))
{
{
if (poolBytes != null)
{
- Array.Clear(poolBytes, 0, data.Length);
- ArrayPool<byte>.Shared.Return(poolBytes);
+ CryptoPool.Return(poolBytes, data.Length);
}
}
}
// See the LICENSE file in the project root for more information.
using System;
-using System.Buffers;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;
}
else
{
- tmp = ArrayPool<byte>.Shared.Rent(decrypted.Length);
+ tmp = CryptoPool.Rent(decrypted.Length);
if (reader.TryCopyOctetStringBytes(tmp, out int written))
{
if (tmp != null)
{
// Already cleared
- ArrayPool<byte>.Shared.Return(tmp);
+ CryptoPool.Return(tmp, clearSize: 0);
}
}
}
{
exception = null;
int encryptedContentLength = encryptedContent.Length;
- byte[] encryptedContentArray = ArrayPool<byte>.Shared.Rent(encryptedContentLength);
+ byte[] encryptedContentArray = CryptoPool.Rent(encryptedContentLength);
try
{
using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm))
using (ICryptoTransform decryptor = alg.CreateDecryptor(cek, alg.IV))
{
+ // If we extend this library to accept additional algorithm providers
+ // then a different array pool needs to be used.
+ Debug.Assert(alg.GetType().Assembly == typeof(Aes).Assembly);
+
return decryptor.OneShot(
encryptedContentArray,
0,
}
finally
{
- Array.Clear(encryptedContentArray, 0, encryptedContentLength);
- ArrayPool<byte>.Shared.Return(encryptedContentArray);
+ CryptoPool.Return(encryptedContentArray, encryptedContentLength);
encryptedContentArray = null;
}
}
// See the LICENSE file in the project root for more information.
using System;
-using System.Buffers;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
try
{
- cek = ArrayPool<byte>.Shared.Rent(privateKey.KeySize / 8);
+ cek = CryptoPool.Rent(privateKey.KeySize / 8);
if (!privateKey.TryDecrypt(encryptedKey, cek, encryptionPadding, out cekLength))
{
{
if (cek != null)
{
- Array.Clear(cek, 0, cekLength);
- ArrayPool<byte>.Shared.Return(cek);
+ CryptoPool.Return(cek, cekLength);
}
}
#else
if (rented != null)
{
- Array.Clear(rented, 0, maxClear);
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, maxClear);
}
return new CspParameters(provType)
{
if (len > buf.Length)
{
- ArrayPool<byte> pool = ArrayPool<byte>.Shared;
-
if (rented != null)
{
- Array.Clear(rented, 0, clearLen);
- pool.Return(rented);
+ CryptoPool.Return(rented, clearLen);
}
- rented = pool.Rent(len);
+ rented = CryptoPool.Rent(len);
buf = rented;
len = rented.Length;
}
<Compile Include="$(CommonPath)\System\Memory\PointerMemoryManager.cs">
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\Oids.cs">
<Link>Common\System\Security\Cryptography\Oids.cs</Link>
</Compile>
int bufSize = 2 * dsaParameters.Q.Length;
#if netcoreapp
- ArrayPool<byte> pool = ArrayPool<byte>.Shared;
- byte[] rented = pool.Rent(bufSize);
+ byte[] rented = CryptoPool.Rent(bufSize);
Span<byte> ieee = new Span<byte>(rented, 0, bufSize);
try
}
finally
{
- ieee.Clear();
- pool.Return(rented);
+ CryptoPool.Return(rented, bufSize);
}
#endif
}
signatureAlgorithm = new Oid(oidValue, oidValue);
#if netcoreapp
- ArrayPool<byte> pool = ArrayPool<byte>.Shared;
// The Q size cannot be bigger than the KeySize.
- byte[] rented = pool.Rent(dsa.KeySize / 8);
+ byte[] rented = CryptoPool.Rent(dsa.KeySize / 8);
int bytesWritten = 0;
try
}
finally
{
- Array.Clear(rented, 0, bytesWritten);
- pool.Return(rented);
+ CryptoPool.Return(rented, bytesWritten);
}
signatureValue = null;
}
#if netcoreapp
- ArrayPool<byte> pool = ArrayPool<byte>.Shared;
- byte[] rented = pool.Rent(bufSize);
+ byte[] rented = CryptoPool.Rent(bufSize);
Span<byte> ieee = new Span<byte>(rented, 0, bufSize);
try
}
finally
{
- ieee.Clear();
- pool.Return(rented);
+ CryptoPool.Return(rented, bufSize);
}
#endif
}
signatureAlgorithm = new Oid(oidValue, oidValue);
#if netcoreapp
- ArrayPool<byte> pool = ArrayPool<byte>.Shared;
-
int bufSize;
checked
{
bufSize = 2 * fieldSize;
}
- byte[] rented = pool.Rent(bufSize);
+ byte[] rented = CryptoPool.Rent(bufSize);
int bytesWritten = 0;
try
}
finally
{
- Array.Clear(rented, 0, bytesWritten);
- pool.Return(rented);
+ CryptoPool.Return(rented, bytesWritten);
}
#endif
ReadOnlySpan<byte> encodedSpan = contentsWriter.EncodeAsSpan();
- rentedAuthSafe = ArrayPool<byte>.Shared.Rent(encodedSpan.Length);
+ rentedAuthSafe = CryptoPool.Rent(encodedSpan.Length);
encodedSpan.CopyTo(rentedAuthSafe);
authSafeSpan = rentedAuthSafe.AsSpan(0, encodedSpan.Length);
// Get an array of the proper size for the hash.
byte[] macKey = hasher.GetHashAndReset();
- rentedMac = ArrayPool<byte>.Shared.Rent(macKey.Length);
+ rentedMac = CryptoPool.Rent(macKey.Length);
macSpan = rentedMac.AsSpan(0, macKey.Length);
// Since the biggest supported hash is SHA-2-512 (64 bytes), the
if (rentedMac != null)
{
- ArrayPool<byte>.Shared.Return(rentedMac);
+ // Already cleared
+ CryptoPool.Return(rentedMac, clearSize: 0);
}
if (rentedAuthSafe != null)
{
- ArrayPool<byte>.Shared.Return(rentedAuthSafe);
+ // Already cleared
+ CryptoPool.Return(rentedAuthSafe, clearSize: 0);
}
}
}
out bool isPkcs12);
int cipherBlockBytes = cipher.BlockSize / 8;
- byte[] encryptedRent = ArrayPool<byte>.Shared.Rent(contentsSpan.Length + cipherBlockBytes);
+ byte[] encryptedRent = CryptoPool.Rent(contentsSpan.Length + cipherBlockBytes);
Span<byte> encryptedSpan = Span<byte>.Empty;
Span<byte> iv = stackalloc byte[cipherBlockBytes];
Span<byte> salt = stackalloc byte[16];
finally
{
CryptographicOperations.ZeroMemory(encryptedSpan);
- ArrayPool<byte>.Shared.Return(encryptedRent);
+ CryptoPool.Return(encryptedRent, clearSize: 0);
writer?.Dispose();
}
}
finally
{
CryptographicOperations.ZeroMemory(decryptedMemory.Span);
- ArrayPool<byte>.Shared.Return(decrypted.Array);
+ CryptoPool.Return(decrypted.Array, clearSize: 0);
}
}
finally
{
CryptographicOperations.ZeroMemory(decryptedMemory.Span);
- ArrayPool<byte>.Shared.Return(decrypted.Array);
+ CryptoPool.Return(decrypted.Array, clearSize: 0);
}
}
return inner;
}
- rented = ArrayPool<byte>.Shared.Rent(wrappedContent.Length);
+ rented = CryptoPool.Rent(wrappedContent.Length);
if (!reader.TryCopyOctetStringBytes(rented, out bytesWritten))
{
{
if (rented != null)
{
- rented.AsSpan(0, bytesWritten).Clear();
- ArrayPool<byte>.Shared.Return(rented);
+ CryptoPool.Return(rented, bytesWritten);
}
}
<Compile Include="$(CommonPath)\Internal\Cryptography\Helpers.cs">
<Link>Internal\Cryptography\Helpers.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\KeySizeHelpers.cs">
<Link>Common\System\Security\Cryptography\KeySizeHelpers.cs</Link>
</Compile>
<ItemGroup>
<Reference Include="System.Buffers" />
<Reference Include="System.Diagnostics.Debug" />
+ <Reference Include="System.Memory" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
<Reference Include="System.Threading" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
-
namespace System.Security.Cryptography
{
public abstract class AsymmetricAlgorithm : IDisposable
while (true)
{
- Span<byte> writtenSpan = Span<byte>.Empty;
- byte[] buf = ArrayPool<byte>.Shared.Rent(bufSize);
+ byte[] buf = CryptoPool.Rent(bufSize);
+ int bytesWritten = 0;
bufSize = buf.Length;
fixed (byte* bufPtr = buf)
{
try
{
- if (exporter(password, pbeParameters, buf, out int bytesWritten))
+ if (exporter(password, pbeParameters, buf, out bytesWritten))
{
- writtenSpan = new Span<byte>(buf, 0, bytesWritten);
+ Span<byte> writtenSpan = new Span<byte>(buf, 0, bytesWritten);
return writtenSpan.ToArray();
}
}
finally
{
- if (writtenSpan.Length > 0)
- {
- CryptographicOperations.ZeroMemory(writtenSpan);
- }
-
- ArrayPool<byte>.Shared.Return(buf);
+ CryptoPool.Return(buf, bytesWritten);
}
bufSize = checked(bufSize * 2);
while (true)
{
- Span<byte> writtenSpan = Span<byte>.Empty;
- byte[] buf = ArrayPool<byte>.Shared.Rent(bufSize);
+ byte[] buf = CryptoPool.Rent(bufSize);
+ int bytesWritten = 0;
bufSize = buf.Length;
fixed (byte* bufPtr = buf)
{
try
{
- if (exporter(buf, out int bytesWritten))
+ if (exporter(buf, out bytesWritten))
{
- writtenSpan = new Span<byte>(buf, 0, bytesWritten);
+ Span<byte> writtenSpan = new Span<byte>(buf, 0, bytesWritten);
return writtenSpan.ToArray();
}
}
finally
{
- if (writtenSpan.Length > 0)
- {
- CryptographicOperations.ZeroMemory(writtenSpan);
- }
-
- ArrayPool<byte>.Shared.Return(buf);
+ CryptoPool.Return(buf, bytesWritten);
}
bufSize = checked(bufSize * 2);
if (blocksToProcess > 1 && _transform.CanTransformMultipleBlocks)
{
int numWholeBlocksInBytes = blocksToProcess * _inputBlockSize;
+
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] tempInputBuffer = ArrayPool<byte>.Shared.Rent(numWholeBlocksInBytes);
byte[] tempOutputBuffer = null;
Buffer.BlockCopy(tempInputBuffer, numWholeReadBlocksInBytes, _inputBuffer, 0, numIgnoredBytes);
}
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
tempOutputBuffer = ArrayPool<byte>.Shared.Rent(numWholeReadBlocks * _outputBlockSize);
numOutputBytes = _transform.TransformBlock(tempInputBuffer, 0, numWholeReadBlocksInBytes, tempOutputBuffer, 0);
Buffer.BlockCopy(tempOutputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
bytesToDeliver -= numOutputBytes;
currentOutputIndex += numOutputBytes;
}
+
+ CryptographicOperations.ZeroMemory(new Span<byte>(tempInputBuffer, 0, numWholeBlocksInBytes));
+ ArrayPool<byte>.Shared.Return(tempInputBuffer);
+ tempInputBuffer = null;
}
- finally
+ catch
{
// If we rented and then an exception happened we don't know how much was written to,
- // clear the whole thing and return it.
+ // clear the whole thing and let it get reclaimed by the GC.
if (tempOutputBuffer != null)
{
CryptographicOperations.ZeroMemory(tempOutputBuffer);
- ArrayPool<byte>.Shared.Return(tempOutputBuffer);
tempOutputBuffer = null;
}
- CryptographicOperations.ZeroMemory(new Span<byte>(tempInputBuffer, 0, numWholeBlocksInBytes));
- ArrayPool<byte>.Shared.Return(tempInputBuffer);
- tempInputBuffer = null;
+ // For the input buffer we know how much was written, so clear that.
+ // But still let it get reclaimed by the GC.
+ if (tempInputBuffer != null)
+ {
+ CryptographicOperations.ZeroMemory(new Span<byte>(tempInputBuffer, 0, numWholeBlocksInBytes));
+ tempInputBuffer = null;
+ }
+
+ throw;
}
}
if (_transform.CanTransformMultipleBlocks && numWholeBlocks > 1)
{
int numWholeBlocksInBytes = numWholeBlocks * _inputBlockSize;
+
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] tempOutputBuffer = ArrayPool<byte>.Shared.Rent(numWholeBlocks * _outputBlockSize);
numOutputBytes = 0;
currentInputIndex += numWholeBlocksInBytes;
bytesToWrite -= numWholeBlocksInBytes;
+ CryptographicOperations.ZeroMemory(new Span<byte>(tempOutputBuffer, 0, numOutputBytes));
+ ArrayPool<byte>.Shared.Return(tempOutputBuffer);
+ tempOutputBuffer = null;
}
- finally
+ catch
{
CryptographicOperations.ZeroMemory(new Span<byte>(tempOutputBuffer, 0, numOutputBytes));
- ArrayPool<byte>.Shared.Return(tempOutputBuffer);
tempOutputBuffer = null;
+ throw;
}
}
else
if (_disposed)
throw new ObjectDisposedException(null);
- // Default the buffer size to 4K.
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
- try
+ int bytesRead;
+ while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
- int bytesRead;
- while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
- {
- HashCore(buffer, 0, bytesRead);
- }
-
- return CaptureHashCodeAndReinitialize();
- }
- finally
- {
- CryptographicOperations.ZeroMemory(buffer);
- ArrayPool<byte>.Shared.Return(buffer);
+ HashCore(buffer, 0, bytesRead);
}
+
+ ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
+ return CaptureHashCodeAndReinitialize();
}
private byte[] CaptureHashCodeAndReinitialize()
protected virtual void HashCore(ReadOnlySpan<byte> source)
{
+ // Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] array = ArrayPool<byte>.Shared.Rent(source.Length);
- try
- {
- source.CopyTo(array);
- HashCore(array, 0, source.Length);
- }
- finally
- {
- Array.Clear(array, 0, source.Length);
- ArrayPool<byte>.Shared.Return(array);
- }
+ source.CopyTo(array);
+ HashCore(array, 0, source.Length);
+ Array.Clear(array, 0, source.Length);
+ ArrayPool<byte>.Shared.Return(array);
}
protected virtual bool TryHashFinal(Span<byte> destination, out int bytesWritten)
}
finally
{
- ArrayPool<byte>.Shared.Return(crlDistributionPoints.Array);
+ // The data came from a certificate, so it's public.
+ CryptoPool.Return(crlDistributionPoints.Array, clearSize: 0);
}
return null;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;
ref _remainingDownloadTime);
// The AIA record is contained in a public structure, so no need to clear it.
- ArrayPool<byte>.Shared.Return(authorityInformationAccess.Array);
+ CryptoPool.Return(authorityInformationAccess.Array, clearSize: 0);
if (downloaded == null)
{
{
int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack);
Span<IntPtr> tempChain = stackalloc IntPtr[DefaultChainCapacity];
- IntPtr[] tempChainRent = null;
+ byte[] tempChainRent = null;
if (chainSize <= tempChain.Length)
{
}
else
{
- tempChainRent = ArrayPool<IntPtr>.Shared.Rent(chainSize);
- tempChain = tempChainRent.AsSpan(0, chainSize);
+ int targetSize = checked(chainSize * IntPtr.Size);
+ tempChainRent = CryptoPool.Rent(targetSize);
+ tempChain = MemoryMarshal.Cast<byte, IntPtr>(tempChainRent.AsSpan(0, targetSize));
}
for (int i = 0; i < chainSize; i++)
if (tempChainRent != null)
{
- // While the IntPtrs aren't secret, clearing them helps prevent
- // accidental use-after-free because of pooling.
- tempChain.Clear();
- ArrayPool<IntPtr>.Shared.Return(tempChainRent);
+ CryptoPool.Return(tempChainRent);
}
}
}
string requestUrl = UrlPathAppend(baseUri, urlEncoded);
// Nothing sensitive is in the encoded request (it was sent via HTTP-non-S)
- ArrayPool<byte>.Shared.Return(encoded.Array);
+ CryptoPool.Return(encoded.Array, clearSize: 0);
ArrayPool<char>.Shared.Return(urlEncoded.Array);
// https://tools.ietf.org/html/rfc6960#appendix-A describes both a GET and a POST
}
string baseUrl = FindHttpAiaRecord(authorityInformationAccess, Oids.OcspEndpoint);
- ArrayPool<byte>.Shared.Return(authorityInformationAccess.Array);
+ CryptoPool.Return(authorityInformationAccess.Array, clearSize: 0);
return baseUrl;
}
if (_errors == null)
{
int size = Math.Max(DefaultChainCapacity, errorDepth + 1);
+ // Since ErrorCollection is a non-public type, this is a private pool.
_errors = ArrayPool<ErrorCollection>.Shared.Rent(size);
// We only do spares writes.
<Compile Include="$(CommonPath)\System\Memory\PointerMemoryManager.cs">
<Link>Common\System\Memory\PointerMemoryManager.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\CryptoPool.cs">
+ <Link>Common\System\Security\Cryptography\CryptoPool.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\Oids.cs">
<Link>Common\System\Security\Cryptography\Oids.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Crypto.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Crypto.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.PooledCrypto.cs">
+ <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.PooledCrypto.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs</Link>
</Compile>