| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/web_package/signed_web_bundles/signature_entry_parser.h" |
| |
| #include "base/containers/extend.h" |
| #include "base/containers/map_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/types/expected_macros.h" |
| #include "components/web_package/input_reader.h" |
| #include "components/web_package/signed_web_bundles/attribute_map_parser.h" |
| #include "components/web_package/signed_web_bundles/constants.h" |
| #include "components/web_package/signed_web_bundles/ecdsa_p256_public_key.h" |
| #include "components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.h" |
| #include "components/web_package/signed_web_bundles/ed25519_public_key.h" |
| #include "components/web_package/signed_web_bundles/ed25519_signature.h" |
| #include "components/web_package/signed_web_bundles/integrity_block_parser.h" |
| #include "components/web_package/signed_web_bundles/types.h" |
| #include "third_party/boringssl/src/include/openssl/curve25519.h" |
| |
| namespace web_package { |
| |
| namespace { |
| |
| using SignatureType = mojom::SignatureInfo::Tag; |
| |
| std::pair<SignatureType, BinaryData> GetSignatureType( |
| const AttributesMap& attributes_map) { |
| const cbor::Value* ed25519_key = |
| base::FindOrNull(attributes_map, kEd25519PublicKeyAttributeName); |
| const cbor::Value* ecdsa_key = |
| base::FindOrNull(attributes_map, kEcdsaP256PublicKeyAttributeName); |
| |
| if (ed25519_key && ecdsa_key) { |
| // The signature type cannot be determined if the attributes map contains |
| // both keys. |
| return {SignatureType::kUnknown, BinaryData()}; |
| } else if (ecdsa_key && ecdsa_key->is_bytestring()) { |
| return {SignatureType::kEcdsaP256Sha256, ecdsa_key->GetBytestring()}; |
| } else if (ed25519_key && ed25519_key->is_bytestring()) { |
| return {SignatureType::kEd25519, ed25519_key->GetBytestring()}; |
| } else { |
| // The signature type cannot be determined neither key is present in the |
| // attributes map or the key is not a valid bytestring. |
| return {SignatureType::kUnknown, BinaryData()}; |
| }; |
| } |
| |
| } // namespace |
| |
| SignatureStackEntryParser::SignatureStackEntryParser( |
| mojom::BundleDataSource& data_source, |
| SignatureEntryParsedCallback callback) |
| : data_source_(data_source), callback_(std::move(callback)) {} |
| |
| SignatureStackEntryParser::~SignatureStackEntryParser() = default; |
| |
| void SignatureStackEntryParser::Parse(uint64_t offset_in_stream) { |
| offset_in_stream_ = offset_in_stream; |
| |
| data_source_->Read( |
| offset_in_stream_, kMaxCBORItemHeaderSize, |
| base::BindOnce(&SignatureStackEntryParser::ReadSignatureStructure, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SignatureStackEntryParser::ReadSignatureStructure( |
| const std::optional<BinaryData>& data) { |
| if (!data) { |
| RunErrorCallback("Error reading signature stack entry."); |
| return; |
| } |
| |
| InputReader input(*data); |
| |
| // Each signature stack entry should be an array with two elements: |
| // attributes and signature |
| const auto array_length = input.ReadCBORHeader(CBORType::kArray); |
| if (!array_length) { |
| RunErrorCallback("Cannot parse the size of signature stack entry."); |
| return; |
| } |
| |
| if (*array_length != 2) { |
| RunErrorCallback( |
| "Each signature stack entry must contain exactly two elements."); |
| return; |
| } |
| |
| signature_stack_entry_ = |
| mojom::BundleIntegrityBlockSignatureStackEntry::New(); |
| |
| offset_in_stream_ += input.CurrentOffset(); |
| |
| attribute_map_parser_ = std::make_unique<AttributeMapParser>( |
| *data_source_, |
| base::BindOnce(&SignatureStackEntryParser::GetAttributesMap, |
| weak_factory_.GetWeakPtr())); |
| |
| attribute_map_parser_->Parse(offset_in_stream_); |
| } |
| |
| void SignatureStackEntryParser::GetAttributesMap( |
| AttributeMapParser::ParsingResult result) { |
| ASSIGN_OR_RETURN((auto [attributes_map, offset_to_end_of_map]), |
| std::move(result), |
| &SignatureStackEntryParser::RunErrorCallback, this); |
| |
| attributes_map_ = std::move(attributes_map); |
| uint64_t attribute_map_size = offset_to_end_of_map - offset_in_stream_; |
| data_source_->Read( |
| offset_in_stream_, attribute_map_size, |
| base::BindOnce(&SignatureStackEntryParser::ReadAttributesMapBytes, |
| weak_factory_.GetWeakPtr(), attribute_map_size)); |
| } |
| |
| void SignatureStackEntryParser::ReadAttributesMapBytes( |
| uint64_t num_bytes, |
| const std::optional<BinaryData>& data) { |
| if (!data) { |
| RunErrorCallback("Error reading signature stack entry."); |
| return; |
| } |
| |
| // Keep track of the raw CBOR bytes of the signature attributes. |
| base::Extend(signature_stack_entry_->attributes_cbor, *data); |
| |
| offset_in_stream_ += num_bytes; |
| data_source_->Read( |
| offset_in_stream_, kMaxCBORItemHeaderSize, |
| base::BindOnce(&SignatureStackEntryParser::ReadSignatureHeader, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SignatureStackEntryParser::ReadSignatureHeader( |
| const std::optional<BinaryData>& data) { |
| if (!data) { |
| RunErrorCallback( |
| "Error reading CBOR header of the signature stack entry's " |
| "signature."); |
| return; |
| } |
| |
| InputReader input(*data); |
| |
| const auto signature_length = input.ReadCBORHeader(CBORType::kByteString); |
| if (!signature_length) { |
| RunErrorCallback( |
| "Cannot parse the size of signature stack entry's signature."); |
| return; |
| } |
| |
| offset_in_stream_ += input.CurrentOffset(); |
| data_source_->Read( |
| offset_in_stream_, *signature_length, |
| base::BindOnce(&SignatureStackEntryParser::ReadSignatureValue, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SignatureStackEntryParser::ReadSignatureValue( |
| const std::optional<BinaryData>& data) { |
| if (!data) { |
| RunErrorCallback("Error reading signature-stack entry signature."); |
| return; |
| } |
| offset_in_stream_ += data->size(); |
| EvaluateSignatureEntry(*data); |
| } |
| |
| void SignatureStackEntryParser::EvaluateSignatureEntry( |
| BinaryData signature_bytes) { |
| auto [signature_type, public_key_bytes] = GetSignatureType(attributes_map_); |
| switch (signature_type) { |
| case SignatureType::kEd25519: { |
| ASSIGN_OR_RETURN(auto public_key, |
| Ed25519PublicKey::Create(public_key_bytes), |
| &SignatureStackEntryParser::RunErrorCallback, this); |
| |
| ASSIGN_OR_RETURN(auto signature, |
| Ed25519Signature::Create(signature_bytes), |
| &SignatureStackEntryParser::RunErrorCallback, this); |
| |
| signature_stack_entry_->signature_info = mojom::SignatureInfo::NewEd25519( |
| mojom::SignatureInfoEd25519::New(public_key, signature)); |
| |
| } break; |
| case SignatureType::kEcdsaP256Sha256: { |
| ASSIGN_OR_RETURN(auto public_key, |
| EcdsaP256PublicKey::Create(public_key_bytes), |
| &SignatureStackEntryParser::RunErrorCallback, this); |
| |
| ASSIGN_OR_RETURN(auto signature, |
| EcdsaP256SHA256Signature::Create(signature_bytes), |
| &SignatureStackEntryParser::RunErrorCallback, this); |
| |
| signature_stack_entry_->signature_info = |
| mojom::SignatureInfo::NewEcdsaP256Sha256( |
| mojom::SignatureInfoEcdsaP256SHA256::New(public_key, signature)); |
| } break; |
| case SignatureType::kUnknown: |
| // Unknown signature cipher type. |
| signature_stack_entry_->signature_info = |
| mojom::SignatureInfo::NewUnknown(mojom::SignatureInfoUnknown::New()); |
| |
| break; |
| } |
| |
| std::move(callback_).Run( |
| std::make_pair(std::move(signature_stack_entry_), offset_in_stream_)); |
| } |
| |
| void SignatureStackEntryParser::RunErrorCallback(std::string message) { |
| std::move(callback_).Run(base::unexpected{std::move(message)}); |
| } |
| |
| } // namespace web_package |