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 if (partition_size_bytes > 0) {
72 command.AddParameter("--partition_size");
73 command.AddParameter(partition_size_bytes);
74 } else {
75 command.AddParameter("--dynamic_partition_size");
76 }
77 return command;
78 }
79
AddHashFooter(const std::string & image_path,const std::string & partition_name,const off_t partition_size_bytes) const80 Result<void> Avb::AddHashFooter(const std::string& image_path,
81 const std::string& partition_name,
82 const off_t partition_size_bytes) const {
83 auto command =
84 GenerateAddHashFooter(image_path, partition_name, partition_size_bytes);
85 int exit_code = command.Start().Wait();
86 CF_EXPECTF(exit_code == 0, "Failure running {} {}. Exited with status {}",
87 command.Executable(), kAddHashFooter, exit_code);
88 return {};
89 }
90
GenerateInfoImage(const std::string & image_path,const SharedFD & output_file) const91 Command Avb::GenerateInfoImage(const std::string& image_path,
92 const SharedFD& output_file) const {
93 Command command(avbtool_path_);
94 command.AddParameter(kInfoImage);
95 command.AddParameter("--image");
96 command.AddParameter(image_path);
97 command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, output_file);
98 return command;
99 }
100
WriteInfoImage(const std::string & image_path,const std::string & output_path) const101 Result<void> Avb::WriteInfoImage(const std::string& image_path,
102 const std::string& output_path) const {
103 auto output_file = SharedFD::Creat(output_path, 0666);
104 CF_EXPECTF(output_file->IsOpen(), "Unable to create {} with error - {}",
105 output_path, output_file->StrError());
106 auto command = GenerateInfoImage(image_path, output_file);
107 int exit_code = command.Start().Wait();
108 CF_EXPECTF(exit_code == 0, "Failure running {} {}. Exited with status {}",
109 command.Executable(), kInfoImage, exit_code);
110 return {};
111 }
112
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)113 Command Avb::GenerateMakeVbMetaImage(
114 const std::string& output_path,
115 const std::vector<ChainPartition>& chained_partitions,
116 const std::vector<std::string>& included_partitions,
117 const std::vector<std::string>& extra_arguments) {
118 Command command(avbtool_path_);
119 command.AddParameter(kMakeVbmetaImage);
120 command.AddParameter("--algorithm");
121 command.AddParameter(algorithm_);
122 command.AddParameter("--key");
123 command.AddParameter(key_);
124 command.AddParameter("--output");
125 command.AddParameter(output_path);
126
127 for (const auto& partition : chained_partitions) {
128 const std::string argument = partition.name + ":" +
129 partition.rollback_index + ":" +
130 partition.key_path;
131 command.AddParameter("--chain_partition");
132 command.AddParameter(argument);
133 }
134 for (const auto& partition : included_partitions) {
135 command.AddParameter("--include_descriptors_from_image");
136 command.AddParameter(partition);
137 }
138 for (const auto& extra_arg : extra_arguments) {
139 command.AddParameter(extra_arg);
140 }
141 return command;
142 }
143
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)144 Result<void> Avb::MakeVbMetaImage(
145 const std::string& output_path,
146 const std::vector<ChainPartition>& chained_partitions,
147 const std::vector<std::string>& included_partitions,
148 const std::vector<std::string>& extra_arguments) {
149 auto command = GenerateMakeVbMetaImage(output_path, chained_partitions,
150 included_partitions, extra_arguments);
151 int exit_code = command.Start().Wait();
152 CF_EXPECTF(exit_code == 0, "Failure running {} {}. Exited with status {}",
153 command.Executable(), kMakeVbmetaImage, exit_code);
154 CF_EXPECT(EnforceVbMetaSize(output_path));
155 return {};
156 }
157
EnforceVbMetaSize(const std::string & path)158 Result<void> EnforceVbMetaSize(const std::string& path) {
159 const auto vbmeta_size = FileSize(path);
160 CF_EXPECT_LE(vbmeta_size, kVbMetaMaxSize);
161 if (vbmeta_size != kVbMetaMaxSize) {
162 auto vbmeta_fd = SharedFD::Open(path, O_RDWR);
163 CF_EXPECTF(vbmeta_fd->IsOpen(), "Unable to open {} with error {}", path,
164 vbmeta_fd->StrError());
165 CF_EXPECTF(vbmeta_fd->Truncate(kVbMetaMaxSize) == 0,
166 "Truncating {} failed with error {}", path,
167 vbmeta_fd->StrError());
168 CF_EXPECTF(vbmeta_fd->Fsync() == 0, "fsync on {} failed with error {}",
169 path, vbmeta_fd->StrError());
170 }
171 return {};
172 }
173
GetDefaultAvb()174 std::unique_ptr<Avb> GetDefaultAvb() {
175 return std::unique_ptr<Avb>(
176 new Avb(AvbToolBinary(), kDefaultAlgorithm, TestKeyRsa4096()));
177 }
178
CuttlefishKeyAvbComponent()179 fruit::Component<Avb> CuttlefishKeyAvbComponent() {
180 return fruit::createComponent().registerProvider(
181 []() -> Avb* { return GetDefaultAvb().release(); });
182 }
183
184 } // namespace cuttlefish
185