1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #include "host/libs/avb/avb.h"
19 
20 #include <fcntl.h>
21 
22 #include <memory>
23 #include <string>
24 
25 #include <fruit/fruit.h>
26 
27 #include "common/libs/fs/shared_fd.h"
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/result.h"
30 #include "common/libs/utils/subprocess.h"
31 #include "host/libs/config/cuttlefish_config.h"
32 #include "host/libs/config/known_paths.h"
33 
34 namespace cuttlefish {
35 namespace {
36 
37 constexpr char kAddHashFooter[] = "add_hash_footer";
38 constexpr char kDefaultAlgorithm[] = "SHA256_RSA4096";
39 constexpr char kInfoImage[] = "info_image";
40 constexpr char kMakeVbmetaImage[] = "make_vbmeta_image";
41 // Taken from external/avb/libavb/avb_slot_verify.c; this define is not in the
42 // headers
43 constexpr size_t kVbMetaMaxSize = 65536ul;
44 
45 }  // namespace
46 
Avb(std::string avbtool_path)47 Avb::Avb(std::string avbtool_path) : avbtool_path_(std::move(avbtool_path)) {}
48 
Avb(std::string avbtool_path,std::string algorithm,std::string key)49 Avb::Avb(std::string avbtool_path, std::string algorithm, std::string key)
50     : avbtool_path_(std::move(avbtool_path)),
51       algorithm_(std::move(algorithm)),
52       key_(std::move(key)) {}
53 
GenerateAddHashFooter(const std::string & image_path,const std::string & partition_name,const off_t partition_size_bytes) const54 Command Avb::GenerateAddHashFooter(const std::string& image_path,
55                                    const std::string& partition_name,
56                                    const off_t partition_size_bytes) const {
57   Command command(avbtool_path_);
58   command.AddParameter(kAddHashFooter);
59   if (!algorithm_.empty()) {
60     command.AddParameter("--algorithm");
61     command.AddParameter(algorithm_);
62   }
63   if (!key_.empty()) {
64     command.AddParameter("--key");
65     command.AddParameter(key_);
66   }
67   command.AddParameter("--image");
68   command.AddParameter(image_path);
69   command.AddParameter("--partition_name");
70   command.AddParameter(partition_name);
71   command.AddParameter("--partition_size");
72   command.AddParameter(partition_size_bytes);
73   return std::move(command);
74 }
75 
AddHashFooter(const std::string & image_path,const std::string & partition_name,const off_t partition_size_bytes) const76 Result<void> Avb::AddHashFooter(const std::string& image_path,
77                                 const std::string& partition_name,
78                                 const off_t partition_size_bytes) const {
79   auto command =
80       GenerateAddHashFooter(image_path, partition_name, partition_size_bytes);
81   int exit_code = command.Start().Wait();
82   CF_EXPECTF(exit_code == 0, "Failure running {} {}. Exited with status {}",
83              command.Executable(), kAddHashFooter, exit_code);
84   return {};
85 }
86 
GenerateInfoImage(const std::string & image_path,const SharedFD & output_file) const87 Command Avb::GenerateInfoImage(const std::string& image_path,
88                                const SharedFD& output_file) const {
89   Command command(avbtool_path_);
90   command.AddParameter(kInfoImage);
91   command.AddParameter("--image");
92   command.AddParameter(image_path);
93   command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, output_file);
94   return command;
95 }
96 
WriteInfoImage(const std::string & image_path,const std::string & output_path) const97 Result<void> Avb::WriteInfoImage(const std::string& image_path,
98                                  const std::string& output_path) const {
99   auto output_file = SharedFD::Creat(output_path, 0666);
100   CF_EXPECTF(output_file->IsOpen(), "Unable to create {} with error - {}",
101              output_path, output_file->StrError());
102   auto command = GenerateInfoImage(image_path, output_file);
103   int exit_code = command.Start().Wait();
104   CF_EXPECTF(exit_code == 0, "Failure running {} {}. Exited with status {}",
105              command.Executable(), kInfoImage, exit_code);
106   return {};
107 }
108 
GenerateMakeVbMetaImage(const std::string & output_path,const std::vector<ChainPartition> & chained_partitions,const std::vector<std::string> & included_partitions,const std::vector<std::string> & extra_arguments)109 Command Avb::GenerateMakeVbMetaImage(
110     const std::string& output_path,
111     const std::vector<ChainPartition>& chained_partitions,
112     const std::vector<std::string>& included_partitions,
113     const std::vector<std::string>& extra_arguments) {
114   Command command(avbtool_path_);
115   command.AddParameter(kMakeVbmetaImage);
116   command.AddParameter("--algorithm");
117   command.AddParameter(algorithm_);
118   command.AddParameter("--key");
119   command.AddParameter(key_);
120   command.AddParameter("--output");
121   command.AddParameter(output_path);
122 
123   for (const auto& partition : chained_partitions) {
124     const std::string argument = partition.name + ":" +
125                                  partition.rollback_index + ":" +
126                                  partition.key_path;
127     command.AddParameter("--chain_partition");
128     command.AddParameter(argument);
129   }
130   for (const auto& partition : included_partitions) {
131     command.AddParameter("--include_descriptors_from_image");
132     command.AddParameter(partition);
133   }
134   for (const auto& extra_arg : extra_arguments) {
135     command.AddParameter(extra_arg);
136   }
137   return command;
138 }
139 
MakeVbMetaImage(const std::string & output_path,const std::vector<ChainPartition> & chained_partitions,const std::vector<std::string> & included_partitions,const std::vector<std::string> & extra_arguments)140 Result<void> Avb::MakeVbMetaImage(
141     const std::string& output_path,
142     const std::vector<ChainPartition>& chained_partitions,
143     const std::vector<std::string>& included_partitions,
144     const std::vector<std::string>& extra_arguments) {
145   auto command = GenerateMakeVbMetaImage(output_path, chained_partitions,
146                                          included_partitions, extra_arguments);
147   int exit_code = command.Start().Wait();
148   CF_EXPECTF(exit_code == 0, "Failure running {} {}. Exited with status {}",
149              command.Executable(), kMakeVbmetaImage, exit_code);
150   CF_EXPECT(EnforceVbMetaSize(output_path));
151   return {};
152 }
153 
EnforceVbMetaSize(const std::string & path)154 Result<void> EnforceVbMetaSize(const std::string& path) {
155   const auto vbmeta_size = FileSize(path);
156   CF_EXPECT_LE(vbmeta_size, kVbMetaMaxSize);
157   if (vbmeta_size != kVbMetaMaxSize) {
158     auto vbmeta_fd = SharedFD::Open(path, O_RDWR);
159     CF_EXPECTF(vbmeta_fd->IsOpen(), "Unable to open {} with error {}", path,
160                vbmeta_fd->StrError());
161     CF_EXPECTF(vbmeta_fd->Truncate(kVbMetaMaxSize) == 0,
162                "Truncating {} failed with error {}", path,
163                vbmeta_fd->StrError());
164     CF_EXPECTF(vbmeta_fd->Fsync() == 0, "fsync on {} failed with error {}",
165                path, vbmeta_fd->StrError());
166   }
167   return {};
168 }
169 
GetDefaultAvb()170 std::unique_ptr<Avb> GetDefaultAvb() {
171   return std::unique_ptr<Avb>(
172       new Avb(AvbToolBinary(), kDefaultAlgorithm, TestKeyRsa4096()));
173 }
174 
CuttlefishKeyAvbComponent()175 fruit::Component<Avb> CuttlefishKeyAvbComponent() {
176   return fruit::createComponent().registerProvider(
177       []() -> Avb* { return GetDefaultAvb().release(); });
178 }
179 
180 } // namespace cuttlefish