2Copyright 2020-2021 Intel Corporation
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
18* @description Intel(R) AMT Setup.bin Parser
19* @author Ylian Saint-Hilaire
23var CreateAmtSetupBinStack = function () {
26 // Intel(R) AMT Setup.bin GUID's
27 var AmtSetupBinSetupGuids = [
28 '\xb5\x16\xfb\x71\x87\xcb\xf9\x4a\xb4\x41\xca\x7b\x38\x35\x78\xf9', // Version 1
29 '\x96\xb2\x81\x58\xcf\x6b\x72\x4c\x8b\x91\xa1\x5e\x51\x2e\x99\xc4', // Version 2
30 '\xa7\xf7\xf6\xc6\x89\xc4\xf6\x47\x93\xed\xe2\xe5\x02\x0d\xa5\x1d', // Version 3
31 '\xaa\xa9\x34\x52\xe1\x29\xa9\x44\x8d\x4d\x08\x1c\x07\xb9\x63\x53' // Version 4
34 // Notes about version 2 of setup.bin:
35 // - Default "admin" must be followed by a new MEBx password
36 // - ME_VARIABLE_IDENTIFIER_MANAGEABILITY_FEATURE_SELECTION may not appear after any CM settings
37 // - CM_VARIABLE_IDENTIFIER_USER_DEFINED_CERT_ADD must be preceded by setting CM_VARIABLE_IDENTIFIER_USER_DEFINED_CERTS_CONFIG to (TODO!)
40 // - Setup.bin should always start with "CurrentMEBx Pwd", "newMebx Pwd", "manageability selection" (if present).
42 // Intel(R) AMT variable identifiers
43 // Type: 0 = Binar String, 1 = Char, 2 = Short, 3 = Int
44 var AmtSetupBinVarIds =
47 1: [0, "Current MEBx Password"],
48 2: [0, "New MEBx Password"],
49 3: [1, "Manageability Feature Selection",
50 { 0: "None", 1: "Intel AMT" }],
51 4: [1, "Firmware Local Update", // 0 = Disabled, 1 = Enabled, 2 = Password Protected
52 { 0: "Disabled", 1: "Enabled", 2: "Password Protected" }],
53 5: [1, "Firmware Update Qualifier", // 0 = Always, 1 = Never, 2 = Restricted
54 { 0: "Always", 1: "Never", 2: "Restricted" }],
55 6: [4, "Power Package"] // GUID Length (16 bytes), Intel AMT version 2.1, 3 and 4
58 1: [0, "Provisioning Preshared Key ID (PID)"],
59 2: [0, "Provisioning Preshared Key (PPS)"],
60 3: [0, "PKI DNS Suffix"], // 255 bytes max length
61 4: [0, "Configuration Server FQDN"], // 255 bytes max length
62 5: [1, "Remote Configuration Enabled (RCFG)", // 0 = Off, 1 = On
63 { 0: "Off", 1: "On" }],
64 6: [1, "Pre-Installed Certificates Enabled", // 0 = Off, 1 = On
65 { 0: "Off", 1: "On" }],
66 7: [1, "User Defined Certificate Configuration", // 0 = Disabled, 1 = Enabled, 2 = Delete
67 { 0: "Disabled", 1: "Enabled", 2: "Delete" }],
68 8: [0, "User Defined Certificate Addition"], // 1 byte hash algo, 20 to 48 bytes hash, 1 byte name length, up to 32 bytes friendly name, 1 = SHA1 (20 bytes), 2 = SHA256 (32 bytes), 3 = SHA384 (48 bytes). Algo 2 & 3 are for version 3 and up.
69 10: [1, "SOL/IDER Redirection Configuration", {
70 0: "None", 1: "SOL only - User/Pass Disabled", 2: "IDER only - User/Pass Disabled", 3: "SOL+IDER - User/Pass Disabled",
71 4: "None - User/Pass Enabled", 5: "SOL only - User/Pass Enabled", 6: "IDER only - User/Pass Enabled", 7: "SOL+IDER - User/Pass Enabled"
73 11: [0, "Hostname"], // 63 bytes max length
74 12: [0, "Domain Name"], // 255 bytes max length
75 13: [1, "DHCP", { 1: "Disabled", 2: "Enabled" }],
76 14: [1, "Secure Firmware Update (SFWU)", // 0 = Disabled, 1 = Enabled
77 { 0: "Disabled", 1: "Enabled" }],
79 16: [1, "Provisioning Mode (PM)", // 1 = Enterprise, 2 = Small Buisness (SMB)
80 { 0: "Enterprise", 1: "Small Buisness" }],
81 17: [0, "Provisioning Server Address"],
82 18: [2, "Provision Server Port Number (PSPO)"],
83 19: [0, "Static IPv4 Parameters"],
85 21: [0, "PASS Policy Flag"],
86 22: [0, "IPv6"], // Length is 204 bytes old format, 84 bytes new format, Version 3+ only
87 23: [1, "Shared/Dedicated FQDN", // 0 = Dedicated, 1 = Shared. This option is valid only if configuring the hostname as well
88 { 0: "Dedicated", 1: "Shared" }],
89 24: [1, "Dynamic DNS Update", // 0 = Disabled, 1 = Enabled
90 { 0: "Disabled", 1: "Enabled" }],
91 25: [1, "Remote Desktop (KVM) State", // 0 = Disabled, 1 = Enabled
92 { 0: "Disabled", 1: "Enabled" }],
93 26: [1, "Opt-in User Consent Option", // 0 = Disabled, 1 = KVM, 0xFF = ALL
94 { 0: "Disabled", 1: "KVM", 255: "All" }],
95 27: [1, "Opt-in Remote IT Consent Policy", // 0 = Disabled, 1 = Enabled. Allows user consent to be configured remotely.
96 { 0: "Disabled", 1: "Enabled" }],
97 28: [1, "ME Provision Halt/Active", // 0 = Stop, 1 = Start. The "ME provisioning Halt/Activate" command must appear in the file only after "PKIDNSSuffix", "ConfigServerFQDN" and "Provisioning Server Address"
98 { 0: "Stop", 1: "Start" }],
99 29: [1, "Manual Setup and Configuration", // 0 = Automated, 1 = Manual
100 { 0: "Automated", 1: "Manual" }],
101 30: [3, "Support Channel Identifier"], // 4 bytes length. Support channel identifier (valid values: 1-65535)
102 31: [0, "Support Channel Description"], // 60 bytes max. Friendly name used to describe the party representedby the support channel identifier.
103 32: [0, "Service Account Number"], // 32 bytes max. Unique string identifier given to the end user by the service provider.
104 33: [0, "Enrollement Passcode"], // 32 bytes max
105 34: [3, "Service Type"], // 4 bytes length. 1 = Reactive, 2 = Proactive, 4 = One Time Session
106 35: [0, "Service Provider Identifier"] // GUID Length (16 bytes)
111 // Parse the Setup.bin file
112 o.AmtSetupBinCreate = function (version, flags) {
114 obj.fileType = version;
115 obj.recordChunkCount = 1; // TODO
116 obj.recordHeaderByteCount = 46;
117 obj.recordNumber = 0;
118 obj.majorVersion = version;
119 obj.minorVersion = 0;
121 obj.dataRecordsConsumed = 0;
122 obj.dataRecordChunkCount = 1; // TODO
128 // Parse the Setup.bin file
129 o.AmtSetupBinDecode = function (file) {
130 // Format of the setup file header:
131 // FileTypeUUID(16) - uniquely identifies the file type. This identifier will remain valid and constant across all versions of the file type.
132 // RecordChunkCount(2) - indicates the number of 512-byte chunks occupied by this record, including all header, body, and reserved fields.
133 // RecordHeaderBytes(2) - indicates the length of the record header in bytes.
134 // RecordNumber(4) - uniquely identifies the record among all records in the file. The field contains a non-negative ordinal value. The value of this field is always zero in the Local Provisioning File Header Record.
135 // MajorVersion(1) - identifies the major version of the file format specification. This is a positive integer that is greater than or equal to 1. The Major Version number is incremented to indicate that changes have been introduced that will cause code written against a lower Major Version number to fail.
136 // MinorVersion(1) - identifies the minor version of the file format specification. This is an integer that is greater than or equal to 0. The Minor Version number is incremented to indicate that changes have been introduced that will not cause code written against the same Major Version and a lower Minor Version number to fail. The purpose of this behavior is to allow a single local provisioning file to be used for multiple generations of Intel® AMT platform.
137 // Flags (2) - file Flags, 1 = Do not consume records
138 // DataRecordCount(4) - indicates the total number of data records written in the file when it was created.
139 // DataRecordsConsumed(4) - is a counter value that begins at 0 and is incremented by 1 by each platform BIOS when it consumes a data record from the file. This value is used to determine the offset of the next data record in the file.
140 // DataRecordChunkCount(2) - contains the number of 512-byte chunks in each data record. All data records are the same length.
141 // Reserved (2) - reserved
142 // ModuleList - contains a list of module identifiers. A module’s identifier appears in the list if and only if the data records contain entries for that module. Each module identifier is two bytes in length. The list is terminated by an identifier value of 0.
144 var obj = {}, UUID = file.substring(0, 16);
146 for (var i in AmtSetupBinSetupGuids) { if (UUID == AmtSetupBinSetupGuids[i]) obj.fileType = (+i + 1); }
147 if (obj.fileType == 0) return; // Bad header
148 obj.recordChunkCount = ReadShortX(file, 16);
149 obj.recordHeaderByteCount = ReadShortX(file, 18);
150 obj.recordNumber = ReadIntX(file, 20);
151 obj.majorVersion = file.charCodeAt(24);
152 obj.minorVersion = file.charCodeAt(25);
153 obj.flags = ReadShortX(file, 26); // Flags: 1 = Do not consume records
154 var dataRecordCount = ReadIntX(file, 28);
155 obj.dataRecordsConsumed = ReadIntX(file, 32);
156 obj.dataRecordChunkCount = ReadShortX(file, 36);
160 while (ptr + 512 <= file.length) {
162 // Format of a data record header:
163 // RecordTypeIdentifier(4) - identifies the type of record (in this case a data record). Record Identifiers: Invalid - 0, Data Record - 1
164 // RecordFlags(4) - contains a set of bit flags that characterize the record.
165 // RecordChunkCount(2) - contains the number of 512-byte chunks occupied by the record including all header, body, and reserved fields.
166 // RecordHeaderByteCount(2) - indicates the length of the record header in bytes.
167 // RecordNumber(4) - uniquely identifies the record among all records in the file, including invalid as well as valid records. The identifier is a non-negative integer.
170 r.typeIdentifier = ReadIntX(file, ptr);
171 r.flags = ReadIntX(file, ptr + 4); // Flags: 1 = Valid, 2 = Scrambled
172 r.chunkCount = ReadShortX(file, ptr + 8);
173 r.headerByteCount = ReadShortX(file, ptr + 10);
174 r.number = ReadIntX(file, ptr + 12);
177 var ptr2 = 0, recbin = file.substring(ptr + 24, ptr + 512);
178 if ((r.flags & 2) != 0) { recbin = AmtSetupBinDescrambleRecordData(recbin); } // De-Scramble the record
181 // Format of a data record entry:
182 // ModuleIdentifier(2) - identifies the target ME module for the entry.
183 // VariableIdentifier(2) - an enumeration value that identifies the variable. Variable identifiers are unique to each ModuleIdentifier.
184 // VariableLength(2) - is the length of the variable value in bytes.
185 // VariableValue - is the value to be assigned to the variable.
188 v.moduleid = ReadShortX(recbin, ptr2);
189 v.varid = ReadShortX(recbin, ptr2 + 2);
190 if (v.moduleid == 0 || v.varid == 0) break;
191 if (AmtSetupBinVarIds[v.moduleid][v.varid]) {
192 v.length = ReadShortX(recbin, ptr2 + 4);
193 v.type = AmtSetupBinVarIds[v.moduleid][v.varid][0];
194 v.desc = AmtSetupBinVarIds[v.moduleid][v.varid][1];
195 v.value = recbin.substring(ptr2 + 8, ptr2 + 8 + v.length);
196 if (v.type == 1 && v.length == 1) v.value = v.value.charCodeAt(0); // 1 byte number
197 else if (v.type == 2 && v.length == 2) v.value = ReadShortX(v.value, 0); // 2 byte number
198 else if (v.type == 3 && v.length == 4) v.value = ReadIntX(v.value, 0); // 4 byte number
199 else if (v.type == 4) v.value = guidToStr(rstr2hex(v.value)); // GUID
202 ptr2 += (8 + (Math.floor((v.length + 3) / 4) * 4));
205 // Sort the variables
206 r.variables.sort(AmtSetupBinVariableCompare);
212 if (dataRecordCount != obj.records.length) return; // Mismatch record count
216 // Construct a Setup.bin file
217 o.AmtSetupBinEncode = function (obj) {
218 if (obj.fileType < 1 && obj.fileType > AmtSetupBinSetupGuids.length) return null;
219 var out = [], r = AmtSetupBinSetupGuids[obj.fileType - 1], reccount = 0;
221 // Get the list of modules used
222 var modulesInUse = [];
223 for (var i in obj.records) { var rec = obj.records[i]; for (var j in rec.variables) { var v = rec.variables[j]; if (modulesInUse.indexOf(v.moduleid) == -1) { modulesInUse.push(v.moduleid); } } }
225 r += ShortToStrX(obj.recordChunkCount);
226 r += ShortToStrX(42 + (modulesInUse.length * 2)); // Header is 42 bytes long + 2 bytes for each additional modules in use.
227 r += IntToStrX(obj.recordNumber);
228 r += String.fromCharCode(obj.majorVersion, obj.minorVersion);
229 r += ShortToStrX(obj.flags); // Flags: 1 = Do not consume records
230 r += IntToStrX(obj.records.length);
231 r += IntToStrX(obj.dataRecordsConsumed);
232 r += ShortToStrX(obj.dataRecordChunkCount);
233 r += ShortToStrX(0); // Reserved
234 for (var i in modulesInUse) { r += ShortToStrX(modulesInUse[i]); } // Write each module in use. Needs to be null terminated, but the padding that follows will do that.
235 while (r.length < 512) { r += '\0'; } // Pad the header
239 for (var i in obj.records) {
240 var r2 = '', rec = obj.records[i];
241 r2 += IntToStrX(rec.typeIdentifier);
242 r2 += IntToStrX(rec.flags);
243 r2 += IntToStrX(0); // Reserved
244 r2 += IntToStrX(0); // Reserved
245 r2 += ShortToStrX(1); // rec.chunkCount
246 r2 += ShortToStrX(24); // rec.headerByteCount
247 r2 += IntToStrX(++reccount);
249 // Sort the variables
250 rec.variables.sort(AmtSetupBinVariableCompare);
253 // Change variable priority
254 AmtSetupBinMoveToTop(r.variables, 1, 3); // Manageability Feature Selection
255 AmtSetupBinMoveToTop(r.variables, 1, 2); // New MEBx password
256 AmtSetupBinMoveToTop(r.variables, 1, 1); // Current MEBx password
259 // Write each variable
260 for (var j in rec.variables) {
261 var r3 = '', v = rec.variables[j], data = v.value;
262 v.type = AmtSetupBinVarIds[v.moduleid][v.varid][0]; // Set the correct type if not alreay connect
263 if ((v.type > 0) && (v.type < 4)) { // If this is a numeric value, encode it correctly
264 data = parseInt(data);
265 if (v.type == 1) data = String.fromCharCode(data);
266 if (v.type == 2) data = ShortToStrX(data);
267 if (v.type == 3) data = IntToStrX(data);
269 if (v.type == 4) { data = hex2rstr(guidToStr(data.split('-').join('')).split('-').join('')); }
270 r3 += ShortToStrX(v.moduleid); // Module Identifier
271 r3 += ShortToStrX(v.varid); // Variable Identifier
272 r3 += ShortToStrX(data.length); // Variable Length
273 r3 += ShortToStrX(0); // Reserved
274 r3 += data; // Variable Data
275 while (r3.length % 4 != 0) { r3 += '\0'; } // Pad the variable
279 while (r2.length < 512) { r2 += '\0'; } // Pad the record
280 if ((rec.flags & 2) != 0) { r2 = r2.substring(0, 24) + AmtSetupBinScrambleRecordData(r2.substring(24)); } // Scramble the record starting at byte 24, after the header
286 // Used to sort variables
287 function AmtSetupBinVariableCompare(a, b) {
288 if (a.moduleid > b.moduleid) return 1;
289 if (a.moduleid < b.moduleid) return -1;
290 if (a.varid > b.varid) return 1;
291 if (a.varid < b.varid) return -1;
295 // Scramble and un-scramble records
296 function AmtSetupBinScrambleRecordData(data) { var out = ''; for (var i = 0; i < data.length; i++) { out += String.fromCharCode((data.charCodeAt(i) + 17) & 0xFF); } return out; }
297 function AmtSetupBinDescrambleRecordData(data) { var out = ''; for (var i = 0; i < data.length; i++) { out += String.fromCharCode((data.charCodeAt(i) + 0xEF) & 0xFF); } return out; }
299 // Find a moduleid/varid in the variable list, if found, move it to the top
300 //function AmtSetupBinMoveToTop(variables, moduleid, varid) { var i = -1; for (var j in variables) { if ((variables[j].moduleid == moduleid) && (variables[j].varid == varid)) { i = j; } } if (i > 1) { ArrayElementMove(variables, i, 0); } }
302 function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); };
303 function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); };
304 function ReadShortX(v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); };
305 function ReadIntX(v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); };
310module.exports = CreateAmtSetupBinStack;