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); }