public ProtectedMemoryBlob(ISecret secret)
        {
            if (secret == null)
            {
                throw new ArgumentNullException("secret");
            }

            ProtectedMemoryBlob other = secret as ProtectedMemoryBlob;

            if (other != null)
            {
                // Fast-track: simple deep copy scenario.
                this._localAllocHandle = other._localAllocHandle.Duplicate();
                this._plaintextLength  = other._plaintextLength;
            }
            else
            {
                // Copy the secret to a temporary managed buffer, then protect the buffer.
                // We pin the temp buffer and zero it out when we're finished to limit exposure of the secret.
                byte[] tempPlaintextBuffer = new byte[secret.Length];
                fixed(byte *pbTempPlaintextBuffer = tempPlaintextBuffer)
                {
                    try
                    {
                        secret.WriteSecretIntoBuffer(new ArraySegment <byte>(tempPlaintextBuffer));
                        _localAllocHandle = Protect(pbTempPlaintextBuffer, (uint)tempPlaintextBuffer.Length);
                        _plaintextLength  = (uint)tempPlaintextBuffer.Length;
                    }
                    finally
                    {
                        UnsafeBufferUtil.SecureZeroMemory(pbTempPlaintextBuffer, tempPlaintextBuffer.Length);
                    }
                }
            }
        }
        public static ProtectedMemoryBlob Random(int numBytes)
        {
            CryptoUtil.Assert(numBytes >= 0, "numBytes >= 0");

            if (numBytes == 0)
            {
                byte dummy;
                return(new ProtectedMemoryBlob(&dummy, 0));
            }
            else
            {
                // Don't use CNG if we're not on Windows.
                if (!OSVersionUtil.IsBCryptOnWin7OrLaterAvailable())
                {
                    return(new ProtectedMemoryBlob(ManagedGenRandomImpl.Instance.GenRandom(numBytes)));
                }

                byte[] bytes = new byte[numBytes];
                fixed(byte *pbBytes = bytes)
                {
                    try
                    {
                        BCryptUtil.GenRandom(pbBytes, (uint)numBytes);
                        return(new ProtectedMemoryBlob(pbBytes, numBytes));
                    }
                    finally
                    {
                        UnsafeBufferUtil.SecureZeroMemory(pbBytes, numBytes);
                    }
                }
            }
        }
        private void UnprotectInto(byte *pbBuffer)
        {
            // If we're not running on a platform that supports CryptProtectMemory,
            // the handle contains plaintext bytes.
            if (!OSVersionUtil.IsBCryptOnWin7OrLaterAvailable())
            {
                UnsafeBufferUtil.BlockCopy(from: _localAllocHandle, to: pbBuffer, byteCount: _plaintextLength);
                return;
            }

            if (_plaintextLength % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0)
            {
                // Case 1: Secret length is an exact multiple of the block size. Copy directly to the buffer and decrypt there.
                // We go through this code path even for empty plaintexts since we still want SafeHandle dispose semantics.
                UnsafeBufferUtil.BlockCopy(from: _localAllocHandle, to: pbBuffer, byteCount: _plaintextLength);
                MemoryProtection.CryptUnprotectMemory(pbBuffer, _plaintextLength);
            }
            else
            {
                // Case 2: Secret length is not a multiple of the block size. We'll need to duplicate the data and
                // perform the decryption in the duplicate buffer, then copy the plaintext data over.
                using (var duplicateHandle = _localAllocHandle.Duplicate())
                {
                    MemoryProtection.CryptUnprotectMemory(duplicateHandle, checked ((uint)duplicateHandle.Length));
                    UnsafeBufferUtil.BlockCopy(from: duplicateHandle, to: pbBuffer, byteCount: _plaintextLength);
                }
            }
        }
        private static SecureLocalAllocHandle Protect(byte *pbPlaintext, uint cbPlaintext)
        {
            // If we're not running on a platform that supports CryptProtectMemory,
            // shove the plaintext directly into a LocalAlloc handle. Ideally we'd
            // mark this memory page as non-pageable, but this is fraught with peril.
            if (!OSVersionUtil.IsBCryptOnWin7OrLaterAvailable())
            {
                SecureLocalAllocHandle handle = SecureLocalAllocHandle.Allocate((IntPtr) checked ((int)cbPlaintext));
                UnsafeBufferUtil.BlockCopy(from: pbPlaintext, to: handle, byteCount: cbPlaintext);
                return(handle);
            }

            // We need to make sure we're a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE.
            uint numTotalBytesToAllocate = cbPlaintext;
            uint numBytesPaddingRequired = CRYPTPROTECTMEMORY_BLOCK_SIZE - (numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE);

            if (numBytesPaddingRequired == CRYPTPROTECTMEMORY_BLOCK_SIZE)
            {
                numBytesPaddingRequired = 0; // we're already a proper multiple of the block size
            }
            checked { numTotalBytesToAllocate += numBytesPaddingRequired; }
            CryptoUtil.Assert(numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0, "numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0");

            // Allocate and copy plaintext data; padding is uninitialized / undefined.
            SecureLocalAllocHandle encryptedMemoryHandle = SecureLocalAllocHandle.Allocate((IntPtr)numTotalBytesToAllocate);

            UnsafeBufferUtil.BlockCopy(from: pbPlaintext, to: encryptedMemoryHandle, byteCount: cbPlaintext);

            // Finally, CryptProtectMemory the whole mess.
            if (numTotalBytesToAllocate != 0)
            {
                MemoryProtection.CryptProtectMemory(encryptedMemoryHandle, byteCount: numTotalBytesToAllocate);
            }
            return(encryptedMemoryHandle);
        }