blob: a2b5dadad6352a7e200e27e527cabf8ad3a47212 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_response_reader_factory.h"
#include <memory>
#include <string>
#include "base/functional/overloaded.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_validator.h"
#include "chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.h"
#include "chrome/common/url_constants.h"
#include "components/web_package/mojom/web_bundle_parser.mojom.h"
#include "components/web_package/signed_web_bundles/signed_web_bundle_integrity_block.h"
#include "components/web_package/signed_web_bundles/signed_web_bundle_signature_verifier.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
namespace web_app {
IsolatedWebAppResponseReaderFactory::IsolatedWebAppResponseReaderFactory(
std::unique_ptr<IsolatedWebAppValidator> validator,
base::RepeatingCallback<
std::unique_ptr<web_package::SignedWebBundleSignatureVerifier>()>
signature_verifier_factory)
: validator_(std::move(validator)),
signature_verifier_factory_(std::move(signature_verifier_factory)) {}
IsolatedWebAppResponseReaderFactory::~IsolatedWebAppResponseReaderFactory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void IsolatedWebAppResponseReaderFactory::CreateResponseReader(
const base::FilePath& web_bundle_path,
const web_package::SignedWebBundleId& web_bundle_id,
bool skip_signature_verification,
Callback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(web_bundle_id.type(),
web_package::SignedWebBundleId::Type::kEd25519PublicKey);
GURL base_url(
base::StrCat({chrome::kIsolatedAppScheme, url::kStandardSchemeSeparator,
web_bundle_id.id()}));
std::unique_ptr<web_package::SignedWebBundleSignatureVerifier>
signature_verifier = signature_verifier_factory_.Run();
std::unique_ptr<SignedWebBundleReader> reader = SignedWebBundleReader::Create(
web_bundle_path, std::move(base_url), std::move(signature_verifier));
SignedWebBundleReader& reader_ref = *reader.get();
reader_ref.StartReading(
base::BindOnce(&IsolatedWebAppResponseReaderFactory::OnIntegrityBlockRead,
weak_ptr_factory_.GetWeakPtr(), web_bundle_id,
skip_signature_verification),
base::BindOnce(
&IsolatedWebAppResponseReaderFactory::OnIntegrityBlockAndMetadataRead,
weak_ptr_factory_.GetWeakPtr(), std::move(reader), web_bundle_path,
web_bundle_id, std::move(callback)));
}
void IsolatedWebAppResponseReaderFactory::OnIntegrityBlockRead(
const web_package::SignedWebBundleId& web_bundle_id,
bool skip_signature_verification,
const web_package::SignedWebBundleIntegrityBlock integrity_block,
base::OnceCallback<void(SignedWebBundleReader::SignatureVerificationAction)>
integrity_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
validator_->ValidateIntegrityBlock(
web_bundle_id, integrity_block,
base::BindOnce(
&IsolatedWebAppResponseReaderFactory::OnIntegrityBlockValidated,
weak_ptr_factory_.GetWeakPtr(), skip_signature_verification,
std::move(integrity_callback)));
}
void IsolatedWebAppResponseReaderFactory::OnIntegrityBlockValidated(
bool skip_signature_verification,
base::OnceCallback<void(SignedWebBundleReader::SignatureVerificationAction)>
integrity_callback,
absl::optional<std::string> integrity_block_error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (integrity_block_error.has_value()) {
// Aborting parsing will trigger a call to `OnIntegrityBlockAndMetadataRead`
// with a `SignedWebBundleReader::AbortedByCaller` error.
std::move(integrity_callback)
.Run(SignedWebBundleReader::SignatureVerificationAction::Abort(
*integrity_block_error));
return;
}
if (skip_signature_verification) {
std::move(integrity_callback)
.Run(SignedWebBundleReader::SignatureVerificationAction::
ContinueAndSkipSignatureVerification());
} else {
std::move(integrity_callback)
.Run(SignedWebBundleReader::SignatureVerificationAction::
ContinueAndVerifySignatures());
}
}
void IsolatedWebAppResponseReaderFactory::OnIntegrityBlockAndMetadataRead(
std::unique_ptr<SignedWebBundleReader> reader,
const base::FilePath& web_bundle_path,
const web_package::SignedWebBundleId& web_bundle_id,
Callback callback,
absl::optional<SignedWebBundleReader::ReadIntegrityBlockAndMetadataError>
read_integrity_block_and_metadata_error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
absl::optional<std::pair<Error, ReadIntegrityBlockAndMetadataStatus>>
error_and_status;
if (read_integrity_block_and_metadata_error.has_value()) {
Error error = absl::visit(
base::Overloaded{
[](const web_package::mojom::BundleIntegrityBlockParseErrorPtr&
error) -> Error { return error->Clone(); },
[](const SignedWebBundleReader::AbortedByCaller& error) -> Error {
return IntegrityBlockError(error.message);
},
[](const web_package::SignedWebBundleSignatureVerifier::Error&
error) -> Error { return error; },
[](const web_package::mojom::BundleMetadataParseErrorPtr& error)
-> Error { return error->Clone(); },
},
*read_integrity_block_and_metadata_error);
error_and_status = std::make_pair(
std::move(error),
GetStatusFromError(*read_integrity_block_and_metadata_error));
}
if (!error_and_status.has_value()) {
if (auto error_message = validator_->ValidateMetadata(
web_bundle_id, reader->GetPrimaryURL(), reader->GetEntries());
error_message.has_value()) {
error_and_status = std::make_pair(
MetadataError(*error_message),
ReadIntegrityBlockAndMetadataStatus::kMetadataValidationError);
}
}
base::UmaHistogramEnumeration(
"WebApp.Isolated.ReadIntegrityBlockAndMetadataStatus",
error_and_status.has_value()
? error_and_status->second
: ReadIntegrityBlockAndMetadataStatus::kSuccess);
if (error_and_status.has_value()) {
std::move(callback).Run(
base::unexpected(std::move(error_and_status->first)));
return;
}
std::move(callback).Run(
std::make_unique<IsolatedWebAppResponseReader>(std::move(reader)));
}
IsolatedWebAppResponseReaderFactory::ReadIntegrityBlockAndMetadataStatus
IsolatedWebAppResponseReaderFactory::GetStatusFromError(
const SignedWebBundleReader::ReadIntegrityBlockAndMetadataError& error) {
return absl::visit(
base::Overloaded{
[](const web_package::mojom::BundleIntegrityBlockParseErrorPtr&
error) {
switch (error->type) {
case web_package::mojom::BundleParseErrorType::
kParserInternalError:
return ReadIntegrityBlockAndMetadataStatus::
kIntegrityBlockParserInternalError;
case web_package::mojom::BundleParseErrorType::kFormatError:
return ReadIntegrityBlockAndMetadataStatus::
kIntegrityBlockParserFormatError;
case web_package::mojom::BundleParseErrorType::kVersionError:
return ReadIntegrityBlockAndMetadataStatus::
kIntegrityBlockParserVersionError;
}
},
[](const SignedWebBundleReader::AbortedByCaller& error) {
return ReadIntegrityBlockAndMetadataStatus::
kIntegrityBlockValidationError;
},
[](const web_package::SignedWebBundleSignatureVerifier::Error&
error) {
return ReadIntegrityBlockAndMetadataStatus::
kSignatureVerificationError;
},
[](const web_package::mojom::BundleMetadataParseErrorPtr& error) {
switch (error->type) {
case web_package::mojom::BundleParseErrorType::
kParserInternalError:
return ReadIntegrityBlockAndMetadataStatus::
kMetadataParserInternalError;
case web_package::mojom::BundleParseErrorType::kFormatError:
return ReadIntegrityBlockAndMetadataStatus::
kMetadataParserFormatError;
case web_package::mojom::BundleParseErrorType::kVersionError:
return ReadIntegrityBlockAndMetadataStatus::
kMetadataParserVersionError;
}
}},
error);
}
} // namespace web_app