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