Bug 1941046 - Part 4: Send a callback request for impression and clicks of MARS Top...
[gecko.git] / dom / quota / DecryptingInputStream_impl.h
blobe827e642feb37d712789dd8e5a967674b9819f5b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_quota_DecryptingInputStream_impl_h
8 #define mozilla_dom_quota_DecryptingInputStream_impl_h
10 #include "DecryptingInputStream.h"
12 #include <algorithm>
13 #include <cstdio>
14 #include <type_traits>
15 #include <utility>
16 #include "CipherStrategy.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/Result.h"
20 #include "mozilla/ResultExtensions.h"
21 #include "mozilla/Span.h"
22 #include "mozilla/fallible.h"
23 #include "nsDebug.h"
24 #include "nsError.h"
25 #include "nsFileStreams.h"
26 #include "nsID.h"
27 #include "nsIFileStreams.h"
29 namespace mozilla::dom::quota {
31 template <typename CipherStrategy>
32 DecryptingInputStream<CipherStrategy>::DecryptingInputStream(
33 MovingNotNull<nsCOMPtr<nsIInputStream>> aBaseStream, size_t aBlockSize,
34 typename CipherStrategy::KeyType aKey)
35 : DecryptingInputStreamBase(std::move(aBaseStream), aBlockSize),
36 mKey(aKey) {
37 // XXX Move this to a fallible init function.
38 MOZ_ALWAYS_SUCCEEDS(mCipherStrategy.Init(CipherMode::Decrypt,
39 CipherStrategy::SerializeKey(aKey)));
41 // This implementation only supports sync base streams. Verify this in debug
42 // builds.
43 #ifdef DEBUG
44 bool baseNonBlocking;
45 nsresult rv = (*mBaseStream)->IsNonBlocking(&baseNonBlocking);
46 MOZ_ASSERT(NS_SUCCEEDED(rv));
47 MOZ_ASSERT(!baseNonBlocking);
48 #endif
51 template <typename CipherStrategy>
52 DecryptingInputStream<CipherStrategy>::~DecryptingInputStream() {
53 Close();
56 template <typename CipherStrategy>
57 DecryptingInputStream<CipherStrategy>::DecryptingInputStream()
58 : DecryptingInputStreamBase{} {}
60 template <typename CipherStrategy>
61 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::Close() {
62 if (!mBaseStream) {
63 return NS_OK;
66 (*mBaseStream)->Close();
67 mBaseStream.destroy();
69 mPlainBuffer.Clear();
70 mEncryptedBlock.reset();
72 return NS_OK;
75 template <typename CipherStrategy>
76 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::Available(
77 uint64_t* aLengthOut) {
78 if (!mBaseStream) {
79 return NS_BASE_STREAM_CLOSED;
82 int64_t oldPos, endPos;
83 nsresult rv = Tell(&oldPos);
84 if (NS_WARN_IF(NS_FAILED(rv))) {
85 return rv;
88 rv = Seek(SEEK_END, 0);
89 if (NS_WARN_IF(NS_FAILED(rv))) {
90 return rv;
93 rv = Tell(&endPos);
94 if (NS_WARN_IF(NS_FAILED(rv))) {
95 return rv;
98 rv = Seek(SEEK_SET, oldPos);
99 if (NS_WARN_IF(NS_FAILED(rv))) {
100 return rv;
103 *aLengthOut = endPos - oldPos;
104 return NS_OK;
107 template <typename CipherStrategy>
108 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::StreamStatus() {
109 return mBaseStream ? NS_OK : NS_BASE_STREAM_CLOSED;
112 template <typename CipherStrategy>
113 nsresult DecryptingInputStream<CipherStrategy>::BaseStreamStatus() {
114 return mBaseStream ? (*mBaseStream)->StreamStatus() : NS_BASE_STREAM_CLOSED;
117 template <typename CipherStrategy>
118 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::ReadSegments(
119 nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
120 uint32_t* aBytesReadOut) {
121 *aBytesReadOut = 0;
123 if (!mBaseStream) {
124 return NS_BASE_STREAM_CLOSED;
127 nsresult rv;
129 // Do not try to use the base stream's ReadSegments here. Its very
130 // unlikely we will get a single buffer that contains all of the encrypted
131 // data and therefore would have to copy into our own buffer anyways.
132 // Instead, focus on making efficient use of the Read() interface.
134 while (aCount > 0) {
135 // We have some decrypted data in our buffer. Provide it to the callers
136 // writer function.
137 if (mNextByte < mPlainBytes) {
138 MOZ_ASSERT(!mPlainBuffer.IsEmpty());
139 uint32_t remaining = PlainLength();
140 uint32_t numToWrite = std::min(aCount, remaining);
141 uint32_t numWritten;
142 rv = aWriter(this, aClosure,
143 reinterpret_cast<const char*>(&mPlainBuffer[mNextByte]),
144 *aBytesReadOut, numToWrite, &numWritten);
146 // As defined in nsIInputputStream.idl, do not pass writer func errors.
147 if (NS_FAILED(rv)) {
148 return NS_OK;
151 // End-of-file
152 if (numWritten == 0) {
153 return NS_OK;
156 *aBytesReadOut += numWritten;
157 mNextByte += numWritten;
158 MOZ_ASSERT(mNextByte <= mPlainBytes);
160 aCount -= numWritten;
162 continue;
165 // Otherwise decrypt the next chunk and loop. Any resulting data will set
166 // mPlainBytes and mNextByte which we check at the top of the loop.
167 uint32_t bytesRead;
168 rv = ParseNextChunk(false /* aCheckAvailableBytes */, &bytesRead);
169 if (NS_FAILED(rv)) {
170 return rv;
173 // If we couldn't read anything, then this is eof.
174 if (bytesRead == 0) {
175 return NS_OK;
178 mPlainBytes = bytesRead;
179 mNextByte = 0;
182 return NS_OK;
185 template <typename CipherStrategy>
186 nsresult DecryptingInputStream<CipherStrategy>::ParseNextChunk(
187 bool aCheckAvailableBytes, uint32_t* const aBytesReadOut) {
188 *aBytesReadOut = 0;
190 if (!EnsureBuffers()) {
191 return NS_ERROR_OUT_OF_MEMORY;
194 // Read the data to our internal encrypted buffer.
195 auto wholeBlock = mEncryptedBlock->MutableWholeBlock();
196 nsresult rv =
197 ReadAll(AsWritableChars(wholeBlock).Elements(), wholeBlock.Length(),
198 wholeBlock.Length(), aCheckAvailableBytes, aBytesReadOut);
199 if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) {
200 return rv;
203 // XXX Do we need to know the actual decrypted size?
204 rv = mCipherStrategy.Cipher(mEncryptedBlock->MutableCipherPrefix(),
205 mEncryptedBlock->Payload(),
206 AsWritableBytes(Span{mPlainBuffer}));
207 if (NS_WARN_IF(NS_FAILED(rv))) {
208 return rv;
211 *aBytesReadOut = mEncryptedBlock->ActualPayloadLength();
213 return NS_OK;
216 template <typename CipherStrategy>
217 nsresult DecryptingInputStream<CipherStrategy>::ReadAll(
218 char* aBuf, uint32_t aCount, uint32_t aMinValidCount,
219 bool aCheckAvailableBytes, uint32_t* aBytesReadOut) {
220 MOZ_ASSERT(aCount >= aMinValidCount);
221 MOZ_ASSERT(mBaseStream);
223 nsresult rv = NS_OK;
224 *aBytesReadOut = 0;
226 uint32_t offset = 0;
227 while (aCount > 0) {
228 Maybe<uint64_t> availableBytes;
229 if (aCheckAvailableBytes) {
230 uint64_t available;
231 rv = (*mBaseStream)->Available(&available);
232 if (NS_WARN_IF(NS_FAILED(rv))) {
233 if (rv == NS_BASE_STREAM_CLOSED) {
234 rv = NS_OK;
236 break;
239 if (available == 0) {
240 break;
243 availableBytes = Some(available);
246 uint32_t bytesRead = 0;
247 rv = (*mBaseStream)->Read(aBuf + offset, aCount, &bytesRead);
248 if (NS_WARN_IF(NS_FAILED(rv))) {
249 break;
252 // EOF, but don't immediately return. We need to validate min read bytes
253 // below.
254 if (bytesRead == 0) {
255 break;
258 MOZ_DIAGNOSTIC_ASSERT(!availableBytes || bytesRead <= *availableBytes);
260 *aBytesReadOut += bytesRead;
261 offset += bytesRead;
262 aCount -= bytesRead;
265 // Reading zero bytes is not an error. Its the expected EOF condition.
266 // Only compare to the minimum valid count if we read at least one byte.
267 if (*aBytesReadOut != 0 && *aBytesReadOut < aMinValidCount) {
268 return NS_ERROR_CORRUPTED_CONTENT;
271 return rv;
274 template <typename CipherStrategy>
275 bool DecryptingInputStream<CipherStrategy>::EnsureBuffers() {
276 // Lazily create our two buffers so we can report OOM during stream
277 // operation. These allocations only happens once. The buffers are reused
278 // until the stream is closed.
279 if (!mEncryptedBlock) {
280 // XXX Do we need to do this fallible (as the comment above suggests)?
281 mEncryptedBlock.emplace(*mBlockSize);
283 MOZ_ASSERT(mPlainBuffer.IsEmpty());
284 if (NS_WARN_IF(!mPlainBuffer.SetLength(mEncryptedBlock->MaxPayloadLength(),
285 fallible))) {
286 return false;
290 return true;
293 template <typename CipherStrategy>
294 nsresult DecryptingInputStream<CipherStrategy>::EnsureDecryptedStreamSize() {
295 if (mDecryptedStreamSize) {
296 return NS_OK;
299 auto decryptedStreamSizeOrErr = [this]() -> Result<int64_t, nsresult> {
300 nsresult rv = (*mBaseSeekableStream)->Seek(NS_SEEK_SET, 0);
301 if (NS_WARN_IF(NS_FAILED(rv))) {
302 return Err(rv);
305 uint64_t baseStreamSize;
306 rv = (*mBaseStream)->Available(&baseStreamSize);
307 if (NS_WARN_IF(NS_FAILED(rv))) {
308 return Err(rv);
311 if (!baseStreamSize) {
312 return 0;
315 rv = (*mBaseSeekableStream)
316 ->Seek(NS_SEEK_END, -static_cast<int64_t>(*mBlockSize));
317 if (NS_WARN_IF(NS_FAILED(rv))) {
318 return Err(rv);
321 uint32_t bytesRead;
322 rv = ParseNextChunk(true /* aCheckAvailableBytes */, &bytesRead);
323 if (NS_WARN_IF(NS_FAILED(rv))) {
324 return Err(rv);
326 MOZ_ASSERT(bytesRead);
328 mPlainBytes = bytesRead;
330 mNextByte = bytesRead;
332 int64_t current;
333 rv = Tell(&current);
334 if (NS_WARN_IF(NS_FAILED(rv))) {
335 return Err(rv);
338 return current;
339 }();
341 if (decryptedStreamSizeOrErr.isErr()) {
342 return decryptedStreamSizeOrErr.unwrapErr();
345 mDecryptedStreamSize.init(decryptedStreamSizeOrErr.inspect());
347 return NS_OK;
350 template <typename CipherStrategy>
351 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::Tell(
352 int64_t* const aRetval) {
353 MOZ_ASSERT(aRetval);
355 if (!mBaseStream) {
356 return NS_BASE_STREAM_CLOSED;
359 if (!EnsureBuffers()) {
360 return NS_ERROR_OUT_OF_MEMORY;
363 int64_t basePosition;
364 nsresult rv = (*mBaseSeekableStream)->Tell(&basePosition);
365 if (NS_WARN_IF(NS_FAILED(rv))) {
366 return rv;
369 if (basePosition == 0) {
370 *aRetval = 0;
371 return NS_OK;
374 MOZ_ASSERT(0 == basePosition % *mBlockSize);
376 const auto fullBlocks = basePosition / *mBlockSize;
377 MOZ_ASSERT(fullBlocks);
379 *aRetval = (fullBlocks - 1) * mEncryptedBlock->MaxPayloadLength() + mNextByte;
380 return NS_OK;
383 template <typename CipherStrategy>
384 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::Seek(const int32_t aWhence,
385 int64_t aOffset) {
386 if (!mBaseStream) {
387 return NS_BASE_STREAM_CLOSED;
390 if (!EnsureBuffers()) {
391 return NS_ERROR_OUT_OF_MEMORY;
394 int64_t baseCurrent;
395 nsresult rv = (*mBaseSeekableStream)->Tell(&baseCurrent);
396 if (NS_WARN_IF(NS_FAILED(rv))) {
397 return Err(rv);
400 // Can't call this just in NS_SEEK_CUR case, because ensuring the decrypted
401 // size below may change the current position.
402 int64_t current;
403 rv = Tell(&current);
404 if (NS_WARN_IF(NS_FAILED(rv))) {
405 return rv;
408 // If there's a failure we need to restore any previous state.
409 auto autoRestorePreviousState =
410 MakeScopeExit([baseSeekableStream = *mBaseSeekableStream,
411 savedBaseCurrent = baseCurrent,
412 savedPlainBytes = mPlainBytes, savedNextByte = mNextByte,
413 &plainBytes = mPlainBytes, &nextByte = mNextByte] {
414 nsresult rv = baseSeekableStream->Seek(NS_SEEK_SET, savedBaseCurrent);
415 Unused << NS_WARN_IF(NS_FAILED(rv));
416 plainBytes = savedPlainBytes;
417 nextByte = savedNextByte;
420 rv = EnsureDecryptedStreamSize();
421 if (NS_WARN_IF(NS_FAILED(rv))) {
422 return rv;
425 int64_t baseBlocksOffset;
426 int64_t nextByteOffset;
427 switch (aWhence) {
428 case NS_SEEK_CUR:
429 // XXX Simplify this without using Tell.
430 aOffset += current;
431 break;
433 case NS_SEEK_SET:
434 break;
436 case NS_SEEK_END:
437 // XXX Simplify this without using Seek/Tell.
438 aOffset += *mDecryptedStreamSize;
439 break;
441 default:
442 return NS_ERROR_ILLEGAL_VALUE;
445 if (aOffset < 0 || aOffset > *mDecryptedStreamSize) {
446 return NS_ERROR_ILLEGAL_VALUE;
449 baseBlocksOffset = aOffset / mEncryptedBlock->MaxPayloadLength();
450 nextByteOffset = aOffset % mEncryptedBlock->MaxPayloadLength();
452 // XXX If we remain in the same block as before, we can skip this.
453 rv =
454 (*mBaseSeekableStream)->Seek(NS_SEEK_SET, baseBlocksOffset * *mBlockSize);
455 if (NS_WARN_IF(NS_FAILED(rv))) {
456 return rv;
459 uint32_t readBytes;
460 rv = ParseNextChunk(true /* aCheckAvailableBytes */, &readBytes);
461 if (NS_WARN_IF(NS_FAILED(rv))) {
462 return rv;
465 if (readBytes == 0 && baseBlocksOffset != 0) {
466 mPlainBytes = mEncryptedBlock->MaxPayloadLength();
467 mNextByte = mEncryptedBlock->MaxPayloadLength();
468 } else {
469 mPlainBytes = readBytes;
470 mNextByte = nextByteOffset;
473 autoRestorePreviousState.release();
475 return NS_OK;
478 template <typename CipherStrategy>
479 NS_IMETHODIMP DecryptingInputStream<CipherStrategy>::Clone(
480 nsIInputStream** _retval) {
481 if (!mBaseStream) {
482 return NS_BASE_STREAM_CLOSED;
485 if (!(*mBaseCloneableInputStream)->GetCloneable()) {
486 return NS_ERROR_FAILURE;
489 nsCOMPtr<nsIInputStream> clonedStream;
490 nsresult rv =
491 (*mBaseCloneableInputStream)->Clone(getter_AddRefs(clonedStream));
492 if (NS_WARN_IF(NS_FAILED(rv))) {
493 return rv;
496 *_retval = MakeAndAddRef<DecryptingInputStream>(
497 WrapNotNull(std::move(clonedStream)), *mBlockSize, *mKey)
498 .take();
500 return NS_OK;
503 template <typename CipherStrategy>
504 void DecryptingInputStream<CipherStrategy>::Serialize(
505 mozilla::ipc::InputStreamParams& aParams, uint32_t aMaxSize,
506 uint32_t* aSizeUsed) {
507 MOZ_ASSERT(mBaseStream);
508 MOZ_ASSERT(mBaseIPCSerializableInputStream);
510 mozilla::ipc::InputStreamParams baseStreamParams;
511 (*mBaseIPCSerializableInputStream)
512 ->Serialize(baseStreamParams, aMaxSize, aSizeUsed);
514 MOZ_ASSERT(baseStreamParams.type() ==
515 mozilla::ipc::InputStreamParams::TFileInputStreamParams);
517 mozilla::ipc::EncryptedFileInputStreamParams encryptedFileInputStreamParams;
518 encryptedFileInputStreamParams.fileInputStreamParams() =
519 std::move(baseStreamParams);
520 encryptedFileInputStreamParams.key().AppendElements(
521 mCipherStrategy.SerializeKey(*mKey));
522 encryptedFileInputStreamParams.blockSize() = *mBlockSize;
524 aParams = std::move(encryptedFileInputStreamParams);
527 template <typename CipherStrategy>
528 bool DecryptingInputStream<CipherStrategy>::Deserialize(
529 const mozilla::ipc::InputStreamParams& aParams) {
530 MOZ_ASSERT(aParams.type() ==
531 mozilla::ipc::InputStreamParams::TEncryptedFileInputStreamParams);
532 const auto& params = aParams.get_EncryptedFileInputStreamParams();
534 nsCOMPtr<nsIFileInputStream> stream;
535 nsFileInputStream::Create(NS_GET_IID(nsIFileInputStream),
536 getter_AddRefs(stream));
537 nsCOMPtr<nsIIPCSerializableInputStream> baseSerializable =
538 do_QueryInterface(stream);
540 if (NS_WARN_IF(
541 !baseSerializable->Deserialize(params.fileInputStreamParams()))) {
542 return false;
545 Init(WrapNotNull<nsCOMPtr<nsIInputStream>>(std::move(stream)),
546 params.blockSize());
548 auto key = mCipherStrategy.DeserializeKey(params.key());
549 if (NS_WARN_IF(!key)) {
550 return false;
553 mKey.init(*key);
554 if (NS_WARN_IF(
555 NS_FAILED(mCipherStrategy.Init(CipherMode::Decrypt, params.key())))) {
556 return false;
559 return true;
562 } // namespace mozilla::dom::quota
564 #endif