1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <brillo/imageloader/manifest.h>
6
7 #include <memory>
8 #include <utility>
9
10 #include <base/json/json_string_value_serializer.h>
11 #include <base/strings/string_number_conversions.h>
12
13 namespace brillo {
14 namespace imageloader {
15
16 namespace {
17 // The current version of the manifest file.
18 constexpr int kCurrentManifestVersion = 1;
19 // The name of the version field in the manifest.
20 constexpr char kManifestVersionField[] = "manifest-version";
21 // The name of the component version field in the manifest.
22 constexpr char kVersionField[] = "version";
23 // The name of the field containing the image hash.
24 constexpr char kImageHashField[] = "image-sha256-hash";
25 // The name of the bool field indicating whether component is removable.
26 constexpr char kIsRemovableField[] = "is-removable";
27 // The name of the metadata field.
28 constexpr char kMetadataField[] = "metadata";
29 // The name of the field containing the table hash.
30 constexpr char kTableHashField[] = "table-sha256-hash";
31 // The name of the optional field containing the file system type.
32 constexpr char kFSType[] = "fs-type";
33
GetSHA256FromString(const std::string & hash_str,std::vector<uint8_t> * bytes)34 bool GetSHA256FromString(const std::string& hash_str,
35 std::vector<uint8_t>* bytes) {
36 if (!base::HexStringToBytes(hash_str, bytes))
37 return false;
38 return bytes->size() == 32;
39 }
40
41 // Ensure the metadata entry is a dictionary mapping strings to strings and
42 // parse it into |out_metadata| and return true if so.
ParseMetadata(const base::Value * metadata_element,std::map<std::string,std::string> * out_metadata)43 bool ParseMetadata(const base::Value* metadata_element,
44 std::map<std::string, std::string>* out_metadata) {
45 DCHECK(out_metadata);
46
47 const base::DictionaryValue* metadata_dict = nullptr;
48 if (!metadata_element->GetAsDictionary(&metadata_dict))
49 return false;
50
51 base::DictionaryValue::Iterator it(*metadata_dict);
52 for (; !it.IsAtEnd(); it.Advance()) {
53 std::string parsed_value;
54 if (!it.value().GetAsString(&parsed_value)) {
55 LOG(ERROR) << "Key \"" << it.key() << "\" did not map to string value";
56 return false;
57 }
58
59 (*out_metadata)[it.key()] = std::move(parsed_value);
60 }
61
62 return true;
63 }
64
65 } // namespace
66
Manifest()67 Manifest::Manifest() {}
68
ParseManifest(const std::string & manifest_raw)69 bool Manifest::ParseManifest(const std::string& manifest_raw) {
70 // Now deserialize the manifest json and read out the rest of the component.
71 int error_code;
72 std::string error_message;
73 JSONStringValueDeserializer deserializer(manifest_raw);
74 std::unique_ptr<base::Value> value =
75 deserializer.Deserialize(&error_code, &error_message);
76
77 if (!value) {
78 LOG(ERROR) << "Could not deserialize the manifest file. Error "
79 << error_code << ": " << error_message;
80 return false;
81 }
82
83 base::DictionaryValue* manifest_dict = nullptr;
84 if (!value->GetAsDictionary(&manifest_dict)) {
85 LOG(ERROR) << "Could not parse manifest file as JSON.";
86 return false;
87 }
88
89 // This will have to be changed if the manifest version is bumped.
90 int version;
91 if (!manifest_dict->GetInteger(kManifestVersionField, &version)) {
92 LOG(ERROR) << "Could not parse manifest version field from manifest.";
93 return false;
94 }
95 if (version != kCurrentManifestVersion) {
96 LOG(ERROR) << "Unsupported version of the manifest.";
97 return false;
98 }
99 manifest_version_ = version;
100
101 std::string image_hash_str;
102 if (!manifest_dict->GetString(kImageHashField, &image_hash_str)) {
103 LOG(ERROR) << "Could not parse image hash from manifest.";
104 return false;
105 }
106
107 if (!GetSHA256FromString(image_hash_str, &(image_sha256_))) {
108 LOG(ERROR) << "Could not convert image hash to bytes.";
109 return false;
110 }
111
112 std::string table_hash_str;
113 if (!manifest_dict->GetString(kTableHashField, &table_hash_str)) {
114 LOG(ERROR) << "Could not parse table hash from manifest.";
115 return false;
116 }
117
118 if (!GetSHA256FromString(table_hash_str, &(table_sha256_))) {
119 LOG(ERROR) << "Could not convert table hash to bytes.";
120 return false;
121 }
122
123 if (!manifest_dict->GetString(kVersionField, &(version_))) {
124 LOG(ERROR) << "Could not parse component version from manifest.";
125 return false;
126 }
127
128 // The fs_type field is optional, and squashfs by default.
129 fs_type_ = FileSystem::kSquashFS;
130 std::string fs_type;
131 if (manifest_dict->GetString(kFSType, &fs_type)) {
132 if (fs_type == "ext4") {
133 fs_type_ = FileSystem::kExt4;
134 } else if (fs_type == "squashfs") {
135 fs_type_ = FileSystem::kSquashFS;
136 } else {
137 LOG(ERROR) << "Unsupported file system type: " << fs_type;
138 return false;
139 }
140 }
141
142 if (!manifest_dict->GetBoolean(kIsRemovableField, &(is_removable_))) {
143 // If is_removable field does not exist, by default it is false.
144 is_removable_ = false;
145 }
146
147 // Copy out the metadata, if it's there.
148 const base::Value* metadata = nullptr;
149 if (manifest_dict->Get(kMetadataField, &metadata)) {
150 if (!ParseMetadata(metadata, &(metadata_))) {
151 LOG(ERROR) << "Manifest metadata was malformed";
152 return false;
153 }
154 }
155
156 return true;
157 }
158
manifest_version() const159 int Manifest::manifest_version() const {
160 return manifest_version_;
161 }
162
image_sha256() const163 const std::vector<uint8_t>& Manifest::image_sha256() const {
164 return image_sha256_;
165 }
166
table_sha256() const167 const std::vector<uint8_t>& Manifest::table_sha256() const {
168 return table_sha256_;
169 }
170
version() const171 const std::string& Manifest::version() const {
172 return version_;
173 }
174
fs_type() const175 FileSystem Manifest::fs_type() const {
176 return fs_type_;
177 }
178
is_removable() const179 bool Manifest::is_removable() const {
180 return is_removable_;
181 }
182
metadata() const183 const std::map<std::string, std::string> Manifest::metadata() const {
184 return metadata_;
185 }
186
187 } // namespace imageloader
188 } // namespace brillo
189