From fde704cdb38ce68e6388dedffbf19abdb0e91bdb Mon Sep 17 00:00:00 2001 From: Simon Giesecke Date: Thu, 25 Jun 2020 13:23:06 +0000 Subject: [PATCH] Bug 1634436 - Make DecryptingInputStream implement nsIIPCSerializableInputStream. r=baku Differential Revision: https://phabricator.services.mozilla.com/D75913 --- dom/quota/DecryptingInputStream.cpp | 32 ++++++++++- dom/quota/DecryptingInputStream.h | 38 +++++++++++-- dom/quota/DecryptingInputStream_impl.h | 77 +++++++++++++++++++++++--- dom/quota/IPCStreamCipherStrategy.h | 16 ++++++ dom/quota/moz.build | 1 + ipc/glue/InputStreamParams.ipdlh | 8 +++ ipc/glue/InputStreamUtils.cpp | 11 ++++ 7 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 dom/quota/IPCStreamCipherStrategy.h diff --git a/dom/quota/DecryptingInputStream.cpp b/dom/quota/DecryptingInputStream.cpp index 5442acde1cd69..e0edae6a047fb 100644 --- a/dom/quota/DecryptingInputStream.cpp +++ b/dom/quota/DecryptingInputStream.cpp @@ -17,12 +17,22 @@ NS_INTERFACE_MAP_BEGIN(DecryptingInputStreamBase) NS_INTERFACE_MAP_ENTRY(nsISeekableStream) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, mBaseCloneableInputStream || !mBaseStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL( + nsIIPCSerializableInputStream, + mBaseIPCSerializableInputStream || !mBaseStream) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) NS_INTERFACE_MAP_END DecryptingInputStreamBase::DecryptingInputStreamBase( - MovingNotNull> aBaseStream, size_t aBlockSize) - : mBaseStream(std::move(aBaseStream)), mBlockSize(aBlockSize) { + MovingNotNull> aBaseStream, size_t aBlockSize) { + Init(std::move(aBaseStream), aBlockSize); +} + +void DecryptingInputStreamBase::Init( + MovingNotNull> aBaseStream, size_t aBlockSize) { + mBlockSize.init(aBlockSize); + mBaseStream.init(std::move(aBaseStream)); + const nsCOMPtr seekableStream = do_QueryInterface(mBaseStream->get()); MOZ_ASSERT(seekableStream && @@ -35,6 +45,14 @@ DecryptingInputStreamBase::DecryptingInputStreamBase( SameCOMIdentity(mBaseStream->get(), cloneableInputStream)) { mBaseCloneableInputStream.init(WrapNotNullUnchecked(cloneableInputStream)); } + + const nsCOMPtr ipcSerializeInputStream = + do_QueryInterface(mBaseStream->get()); + if (ipcSerializeInputStream && + SameCOMIdentity(mBaseStream->get(), ipcSerializeInputStream)) { + mBaseIPCSerializableInputStream.init( + WrapNotNullUnchecked(ipcSerializeInputStream)); + } } NS_IMETHODIMP DecryptingInputStreamBase::Read(char* aBuf, uint32_t aCount, @@ -58,13 +76,21 @@ NS_IMETHODIMP DecryptingInputStreamBase::GetCloneable(bool* aCloneable) { return NS_OK; } +void DecryptingInputStreamBase::Serialize( + mozilla::ipc::InputStreamParams& aParams, + FileDescriptorArray& aFileDescriptors, bool aDelayedStart, + uint32_t aMaxSize, uint32_t* aSizeUsed, + mozilla::ipc::ChildToParentStreamActorManager* aManager) { + MOZ_CRASH("Not implemented"); +} + size_t DecryptingInputStreamBase::PlainLength() const { MOZ_ASSERT(mNextByte <= mPlainBytes); return mPlainBytes - mNextByte; } size_t DecryptingInputStreamBase::EncryptedBufferLength() const { - return mBlockSize; + return *mBlockSize; } } // namespace mozilla::dom::quota diff --git a/dom/quota/DecryptingInputStream.h b/dom/quota/DecryptingInputStream.h index 0688b63d67857..1cfcbd0c1d335 100644 --- a/dom/quota/DecryptingInputStream.h +++ b/dom/quota/DecryptingInputStream.h @@ -11,6 +11,7 @@ #include "nsCOMPtr.h" #include "nsICloneableInputStream.h" #include "nsIInputStream.h" +#include "nsIIPCSerializableInputStream.h" #include "nsISeekableStream.h" #include "EncryptedBlock.h" @@ -19,7 +20,8 @@ namespace mozilla::dom::quota { class DecryptingInputStreamBase : public nsIInputStream, public nsISeekableStream, - public nsICloneableInputStream { + public nsICloneableInputStream, + public nsIIPCSerializableInputStream { public: NS_DECL_THREADSAFE_ISUPPORTS @@ -31,22 +33,37 @@ class DecryptingInputStreamBase : public nsIInputStream, using nsICloneableInputStream::GetCloneable; NS_IMETHOD GetCloneable(bool* aCloneable) final; + using nsIIPCSerializableInputStream::Serialize; + void Serialize(mozilla::ipc::InputStreamParams& aParams, + FileDescriptorArray& aFileDescriptors, bool aDelayedStart, + uint32_t aMaxSize, uint32_t* aSizeUsed, + mozilla::ipc::ChildToParentStreamActorManager* aManager) final; + protected: DecryptingInputStreamBase(MovingNotNull> aBaseStream, size_t aBlockSize); + // For deserialization only. + DecryptingInputStreamBase() = default; + virtual ~DecryptingInputStreamBase() = default; + void Init(MovingNotNull> aBaseStream, + size_t aBlockSize); + // Convenience routine to determine how many bytes of plain data // we currently have in our buffer. size_t PlainLength() const; size_t EncryptedBufferLength() const; - InitializedOnce>> mBaseStream; + LazyInitializedOnceEarlyDestructible>> + mBaseStream; LazyInitializedOnce> mBaseSeekableStream; LazyInitializedOnce> mBaseCloneableInputStream; + LazyInitializedOnce> + mBaseIPCSerializableInputStream; // Number of bytes of plain data in mBuffer. size_t mPlainBytes = 0; @@ -54,7 +71,7 @@ class DecryptingInputStreamBase : public nsIInputStream, // Next byte of mBuffer to return in ReadSegments(). size_t mNextByte = 0; - const size_t mBlockSize; + LazyInitializedOnceNotNull mBlockSize; size_t mLastBlockLength = 0; }; @@ -72,6 +89,9 @@ class DecryptingInputStream final : public DecryptingInputStreamBase { size_t aBlockSize, CipherStrategy aCipherStrategy, typename CipherStrategy::KeyType aKey); + // For deserialization only. + explicit DecryptingInputStream(CipherStrategy aCipherStrategy); + NS_IMETHOD Close() override; NS_IMETHOD Available(uint64_t* _retval) override; NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, @@ -83,6 +103,16 @@ class DecryptingInputStream final : public DecryptingInputStreamBase { NS_IMETHOD Clone(nsIInputStream** _retval) override; + using DecryptingInputStreamBase::Serialize; + void Serialize( + mozilla::ipc::InputStreamParams& aParams, + FileDescriptorArray& aFileDescriptors, bool aDelayedStart, + uint32_t aMaxSize, uint32_t* aSizeUsed, + mozilla::ipc::ParentToChildStreamActorManager* aManager) override; + + bool Deserialize(const mozilla::ipc::InputStreamParams& aParams, + const FileDescriptorArray& aFileDescriptors) override; + private: ~DecryptingInputStream(); @@ -108,7 +138,7 @@ class DecryptingInputStream final : public DecryptingInputStreamBase { bool EnsureBuffers(); const CipherStrategy mCipherStrategy; - const typename CipherStrategy::KeyType mKey; + LazyInitializedOnce mKey; // Buffer to hold encrypted data. Must copy here since we need a // flat buffer to run the decryption process on. diff --git a/dom/quota/DecryptingInputStream_impl.h b/dom/quota/DecryptingInputStream_impl.h index 91500419a4580..dd1fc3ef33041 100644 --- a/dom/quota/DecryptingInputStream_impl.h +++ b/dom/quota/DecryptingInputStream_impl.h @@ -11,6 +11,8 @@ #include "CipherStrategy.h" +#include "mozilla/ipc/InputStreamParams.h" +#include "nsFileStreams.h" #include "nsIAsyncInputStream.h" #include "nsStreamUtils.h" @@ -38,6 +40,12 @@ DecryptingInputStream::~DecryptingInputStream() { Close(); } +template +DecryptingInputStream::DecryptingInputStream( + CipherStrategy aCipherStrategy) + : DecryptingInputStreamBase{}, + mCipherStrategy(std::move(aCipherStrategy)) {} + template NS_IMETHODIMP DecryptingInputStream::Close() { if (!mBaseStream) { @@ -183,7 +191,7 @@ nsresult DecryptingInputStream::ParseNextChunk( // XXX Do we need to know the actual decrypted size? rv = mCipherStrategy.Cipher( - CipherMode::Decrypt, mKey, mEncryptedBlock->MutableCipherPrefix(), + CipherMode::Decrypt, *mKey, mEncryptedBlock->MutableCipherPrefix(), mEncryptedBlock->Payload(), AsWritableBytes(Span{mPlainBuffer})); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -238,7 +246,7 @@ bool DecryptingInputStream::EnsureBuffers() { // until the stream is closed. if (!mEncryptedBlock) { // XXX Do we need to do this fallible (as the comment above suggests)? - mEncryptedBlock.emplace(mBlockSize); + mEncryptedBlock.emplace(*mBlockSize); MOZ_ASSERT(mPlainBuffer.IsEmpty()); if (NS_WARN_IF(!mPlainBuffer.SetLength(mEncryptedBlock->MaxPayloadLength(), @@ -269,8 +277,8 @@ NS_IMETHODIMP DecryptingInputStream::Tell( return rv; } - const auto fullBlocks = basePosition / mBlockSize; - MOZ_ASSERT(0 == basePosition % mBlockSize); + const auto fullBlocks = basePosition / *mBlockSize; + MOZ_ASSERT(0 == basePosition % *mBlockSize); *aRetval = (fullBlocks - ((mPlainBytes || mLastBlockLength) ? 1 : 0)) * mEncryptedBlock->MaxPayloadLength() + @@ -331,7 +339,7 @@ NS_IMETHODIMP DecryptingInputStream::Seek(const int32_t aWhence, nsresult rv = (*mBaseSeekableStream) - ->Seek(NS_SEEK_END, -static_cast(mBlockSize)); + ->Seek(NS_SEEK_END, -static_cast(*mBlockSize)); if (NS_WARN_IF(NS_FAILED(rv))) { return Err(rv); } @@ -377,7 +385,7 @@ NS_IMETHODIMP DecryptingInputStream::Seek(const int32_t aWhence, // XXX If we remain in the same block as before, we can skip this. nsresult rv = - (*mBaseSeekableStream)->Seek(NS_SEEK_SET, baseBlocksOffset * mBlockSize); + (*mBaseSeekableStream)->Seek(NS_SEEK_SET, baseBlocksOffset * *mBlockSize); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -400,7 +408,7 @@ NS_IMETHODIMP DecryptingInputStream::Seek(const int32_t aWhence, return aOffset == 0 ? NS_OK : NS_ERROR_ILLEGAL_VALUE; } - nsresult rv = (*mBaseSeekableStream)->Seek(NS_SEEK_CUR, -mBlockSize); + nsresult rv = (*mBaseSeekableStream)->Seek(NS_SEEK_CUR, -*mBlockSize); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -438,12 +446,65 @@ NS_IMETHODIMP DecryptingInputStream::Clone( *_retval = MakeAndAddRef(WrapNotNull(std::move(clonedStream)), - mBlockSize, mCipherStrategy, mKey) + *mBlockSize, mCipherStrategy, *mKey) .take(); return NS_OK; } +template +void DecryptingInputStream::Serialize( + mozilla::ipc::InputStreamParams& aParams, + FileDescriptorArray& aFileDescriptors, bool aDelayedStart, + uint32_t aMaxSize, uint32_t* aSizeUsed, + mozilla::ipc::ParentToChildStreamActorManager* aManager) { + MOZ_ASSERT(mBaseStream); + MOZ_ASSERT(mBaseIPCSerializableInputStream); + + mozilla::ipc::InputStreamParams baseStreamParams; + (*mBaseIPCSerializableInputStream) + ->Serialize(baseStreamParams, aFileDescriptors, aDelayedStart, aMaxSize, + aSizeUsed, aManager); + + MOZ_ASSERT(baseStreamParams.type() == + mozilla::ipc::InputStreamParams::TFileInputStreamParams); + + mozilla::ipc::EncryptedFileInputStreamParams encryptedFileInputStreamParams; + encryptedFileInputStreamParams.fileInputStreamParams() = + std::move(baseStreamParams); + encryptedFileInputStreamParams.key().AppendElements( + mCipherStrategy.SerializeKey(*mKey)); + encryptedFileInputStreamParams.blockSize() = *mBlockSize; + + aParams = std::move(encryptedFileInputStreamParams); +} + +template +bool DecryptingInputStream::Deserialize( + const mozilla::ipc::InputStreamParams& aParams, + const FileDescriptorArray& aFileDescriptors) { + MOZ_ASSERT(aParams.type() == + mozilla::ipc::InputStreamParams::TEncryptedFileInputStreamParams); + const auto& params = aParams.get_EncryptedFileInputStreamParams(); + + nsCOMPtr stream; + nsFileInputStream::Create(nullptr, NS_GET_IID(nsIFileInputStream), + getter_AddRefs(stream)); + nsCOMPtr baseSerializable = + do_QueryInterface(stream); + + if (NS_WARN_IF(!baseSerializable->Deserialize(params.fileInputStreamParams(), + aFileDescriptors))) { + return false; + } + + Init(WrapNotNull>(std::move(stream)), + params.blockSize()); + mKey.init(mCipherStrategy.DeserializeKey(params.key())); + + return true; +} + } // namespace mozilla::dom::quota #endif diff --git a/dom/quota/IPCStreamCipherStrategy.h b/dom/quota/IPCStreamCipherStrategy.h new file mode 100644 index 0000000000000..e694358b0c831 --- /dev/null +++ b/dom/quota/IPCStreamCipherStrategy.h @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_quota_IPCStreamCipherStrategy_h +#define mozilla_dom_quota_IPCStreamCipherStrategy_h + +#include "mozilla/dom/quota/DummyCipherStrategy.h" + +namespace mozilla::dom::quota { +using IPCStreamCipherStrategy = DummyCipherStrategy; +} + +#endif diff --git a/dom/quota/moz.build b/dom/quota/moz.build index 2d5185d58b330..392865dd7c275 100644 --- a/dom/quota/moz.build +++ b/dom/quota/moz.build @@ -35,6 +35,7 @@ EXPORTS.mozilla.dom.quota += [ 'EncryptingOutputStream_impl.h', 'FileStreams.h', 'InitializationTypes.h', + 'IPCStreamCipherStrategy.h', 'MemoryOutputStream.h', 'OriginScope.h', 'PersistenceType.h', diff --git a/ipc/glue/InputStreamParams.ipdlh b/ipc/glue/InputStreamParams.ipdlh index f270b56682b62..d38b600c8c8ef 100644 --- a/ipc/glue/InputStreamParams.ipdlh +++ b/ipc/glue/InputStreamParams.ipdlh @@ -92,6 +92,14 @@ union InputStreamParams IPCBlobInputStreamParams; InputStreamLengthWrapperParams; IPCRemoteStreamParams; + EncryptedFileInputStreamParams; +}; + +struct EncryptedFileInputStreamParams +{ + FileInputStreamParams fileInputStreamParams; + uint8_t[] key; + uint32_t blockSize; }; struct BufferedInputStreamParams diff --git a/ipc/glue/InputStreamUtils.cpp b/ipc/glue/InputStreamUtils.cpp index 7b9a64366c535..a83b1b8ce6fae 100644 --- a/ipc/glue/InputStreamUtils.cpp +++ b/ipc/glue/InputStreamUtils.cpp @@ -13,6 +13,8 @@ #include "mozilla/dom/IPCBlobInputStream.h" #include "mozilla/dom/IPCBlobInputStreamChild.h" #include "mozilla/dom/IPCBlobInputStreamStorage.h" +#include "mozilla/dom/quota/DecryptingInputStream_impl.h" +#include "mozilla/dom/quota/IPCStreamCipherStrategy.h" #include "mozilla/ipc/IPCStreamDestination.h" #include "mozilla/ipc/IPCStreamSource.h" #include "mozilla/InputStreamLengthHelper.h" @@ -249,6 +251,9 @@ void InputStreamHelper::PostSerializationActivation(InputStreamParams& aParams, case InputStreamParams::TIPCBlobInputStreamParams: break; + case InputStreamParams::TEncryptedFileInputStreamParams: + break; + default: MOZ_CRASH( "A new stream? Should decide if it must be processed recursively or " @@ -361,6 +366,12 @@ already_AddRefed InputStreamHelper::DeserializeInputStream( serializable = new InputStreamLengthWrapper(); break; + case InputStreamParams::TEncryptedFileInputStreamParams: + serializable = new dom::quota::DecryptingInputStream< + dom::quota::IPCStreamCipherStrategy>( + dom::quota::IPCStreamCipherStrategy{}); + break; + default: MOZ_ASSERT(false, "Unknown params!"); return nullptr;