public override byte *AcquirePagePointer(IPagerLevelTransactionState tx, long pageNumber, PagerState pagerState = null) { var state = GetTransactionState(tx); if (state.LoadedBuffers.TryGetValue(pageNumber, out var buffer)) { return(buffer.Pointer); } var pagePointer = Inner.AcquirePagePointerWithOverflowHandling(tx, pageNumber, pagerState); var pageHeader = (PageHeader *)pagePointer; var size = VirtualPagerLegacyExtensions.GetNumberOfPages(pageHeader) * Constants.Storage.PageSize; buffer = GetBufferAndAddToTxState(pageNumber, state, size); Memory.Copy(buffer.Pointer, pagePointer, buffer.Size); DecryptPage((PageHeader *)buffer.Pointer); if (Sodium.crypto_generichash(buffer.Hash, EncryptionBuffer.HashSize, buffer.Pointer, (ulong)buffer.Size, null, UIntPtr.Zero) != 0) { ThrowInvalidHash(); } return(buffer.Pointer); }
private int EncryptPage(PageHeader *page) { var num = page->PageNumber; var destination = (byte *)page; var subKeyLen = Sodium.crypto_aead_xchacha20poly1305_ietf_keybytes(); var subKey = stackalloc byte[(int)subKeyLen]; fixed(byte *ctx = Context) fixed(byte *mk = _masterKey) { if (Sodium.crypto_kdf_derive_from_key(subKey, subKeyLen, (ulong)num, ctx, mk) != 0) { throw new InvalidOperationException("Unable to generate derived key"); } var dataSize = VirtualPagerLegacyExtensions.GetNumberOfPages(page) * Constants.Storage.PageSize; var npub = (byte *)page + PageHeader.NonceOffset; // here we generate 128(!) bits of random data, but xchacha20poly1305 needs // 192 bits, we go to backward from the radnom nonce to get more bits that // are not really random for the algorithm. Sodium.randombytes_buf(npub, (UIntPtr)(PageHeader.MacOffset - PageHeader.NonceOffset)); ulong macLen = MacLen; var rc = Sodium.crypto_aead_xchacha20poly1305_ietf_encrypt_detached( destination + PageHeader.SizeOf, destination + PageHeader.MacOffset, &macLen, (byte *)page + PageHeader.SizeOf, (ulong)dataSize - PageHeader.SizeOf, (byte *)page, (ulong)PageHeader.NonceOffset, null, // got back a bit to allow for 192 bits nonce, even if the first // 8 bytes aren't really random, the last 128 bits are securely // radnom (byte *)page + PageHeader.NonceOffset - sizeof(long), subKey ); Debug.Assert(macLen == MacLen); if (rc != 0) { throw new InvalidOperationException($"Unable to encrypt page {num}, rc={rc}"); } return(dataSize); } }
private void TxOnCommit(IPagerLevelTransactionState tx) { if (tx.IsWriteTransaction == false) { return; } if (tx.CryptoPagerTransactionState == null) { return; } if (tx.CryptoPagerTransactionState.TryGetValue(this, out var state) == false) { return; } var pageHash = stackalloc byte[EncryptionBuffer.HashSizeInt]; foreach (var buffer in state.LoadedBuffers) { if (buffer.Value.SkipOnTxCommit) { continue; } if (Sodium.crypto_generichash(pageHash, EncryptionBuffer.HashSize, buffer.Value.Pointer, (ulong)buffer.Value.Size, null, UIntPtr.Zero) != 0) { ThrowInvalidHash(); } if (Sodium.sodium_memcmp(pageHash, buffer.Value.Hash, EncryptionBuffer.HashSize) == 0) { continue; // No modification } // Encrypt the local buffer, then copy the encrypted value to the pager var pageHeader = (PageHeader *)buffer.Value.Pointer; var dataSize = EncryptPage(pageHeader); var numPages = VirtualPagerLegacyExtensions.GetNumberOfOverflowPages(dataSize); Inner.EnsureContinuous(buffer.Key, numPages); Inner.EnsureMapped(tx, buffer.Key, numPages); var pagePointer = Inner.AcquirePagePointer(tx, buffer.Key); Memory.Copy(pagePointer, buffer.Value.Pointer, dataSize); } }
protected int CopyPageImpl(I4KbBatchWrites destwI4KbBatchWrites, long p, PagerState pagerState) { var src = AcquirePagePointer(null, p, pagerState); var pageHeader = (PageHeader *)src; int numberOfPages = 1; if ((pageHeader->Flags & PageFlags.Overflow) == PageFlags.Overflow) { numberOfPages = VirtualPagerLegacyExtensions.GetNumberOfOverflowPages(pageHeader->OverflowSize); } const int adjustPageSize = (Constants.Storage.PageSize) / (4 * Constants.Size.Kilobyte); destwI4KbBatchWrites.Write(pageHeader->PageNumber * (long)adjustPageSize, numberOfPages * adjustPageSize, src); return(numberOfPages); }
private void DecryptPage(PageHeader *page) { var num = page->PageNumber; var destination = (byte *)page; var subKeyLen = Sodium.crypto_aead_xchacha20poly1305_ietf_keybytes(); var subKey = stackalloc byte[(int)subKeyLen]; fixed(byte *ctx = Context) fixed(byte *mk = _masterKey) { if (Sodium.crypto_kdf_derive_from_key(subKey, subKeyLen, (ulong)num, ctx, mk) != 0) { throw new InvalidOperationException("Unable to generate derived key"); } var dataSize = (ulong)VirtualPagerLegacyExtensions.GetNumberOfPages(page) * Constants.Storage.PageSize; var rc = Sodium.crypto_aead_xchacha20poly1305_ietf_decrypt_detached( destination + PageHeader.SizeOf, null, (byte *)page + PageHeader.SizeOf, dataSize - PageHeader.SizeOf, (byte *)page + PageHeader.MacOffset, (byte *)page, (ulong)PageHeader.NonceOffset, // we need to go 8 bytes before the nonce to get where // the full nonce (fixed 8 bytes + random 16 bytes). (byte *)page + PageHeader.NonceOffset - sizeof(long), subKey ); if (rc != 0) { throw new InvalidOperationException($"Unable to decrypt page {num}, rc={rc}"); } } }
public override int CopyPage(I4KbBatchWrites destI4KbBatchWrites, long pageNumber, PagerState pagerState) { var distanceFromStart = (pageNumber % NumberOfPagesInAllocationGranularity); var allocationStartPosition = pageNumber - distanceFromStart; var offset = new WindowsMemoryMapPager.SplitValue { Value = (ulong)allocationStartPosition * (ulong)Constants.Storage.PageSize }; var result = MapViewOfFileEx(_hFileMappingObject, _mmFileAccessType, offset.High, offset.Low, (UIntPtr)AllocationGranularity, null); try { if (result == null) { var lastWin32Error = Marshal.GetLastWin32Error(); throw new Win32Exception($"Unable to map (default view size) {AllocationGranularity / Constants.Size.Kilobyte:#,#0} kb for page {pageNumber} starting at {allocationStartPosition} on {FileName}", new Win32Exception(lastWin32Error)); } var pageHeader = (PageHeader *)(result + distanceFromStart * Constants.Storage.PageSize); int numberOfPages = 1; if ((pageHeader->Flags & PageFlags.Overflow) == PageFlags.Overflow) { numberOfPages = VirtualPagerLegacyExtensions.GetNumberOfOverflowPages(pageHeader->OverflowSize); } if (numberOfPages + distanceFromStart > NumberOfPagesInAllocationGranularity) { UnmapViewOfFile(result); result = null; var newSize = NearestSizeToAllocationGranularity((numberOfPages + distanceFromStart) * Constants.Storage.PageSize); result = MapViewOfFileEx(_hFileMappingObject, _mmFileAccessType, offset.High, offset.Low, (UIntPtr)newSize, null); if (result == null) { var lastWin32Error = Marshal.GetLastWin32Error(); throw new Win32Exception($"Unable to map {newSize / Constants.Size.Kilobyte:#,#0} kb for page {pageNumber} starting at {allocationStartPosition} on {FileName}", new Win32Exception(lastWin32Error)); } pageHeader = (PageHeader *)(result + (distanceFromStart * Constants.Storage.PageSize)); } const int adjustPageSize = (Constants.Storage.PageSize) / (4 * Constants.Size.Kilobyte); destI4KbBatchWrites.Write(pageHeader->PageNumber * adjustPageSize, numberOfPages * adjustPageSize, (byte *)pageHeader); return(numberOfPages); } finally { if (result != null) { UnmapViewOfFile(result); } } }