Skip to content

Commit eb2ed5e

Browse files
committed
Fix hash verification
1 parent 31bad7f commit eb2ed5e

File tree

3 files changed

+70
-103
lines changed

3 files changed

+70
-103
lines changed

.github/workflows/build-openwrt.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@ jobs:
7474
7575
- name: Setup configuration and custom files
7676
run: |
77-
# patch files with repository info
77+
# delete unneeded folders and patch files with repository info
78+
rm -frv builder_repo/.git builder_repo/.github
7879
for file in $(grep -rl XXXXXX builder_repo/); do echo "Patching: $file"; sed -i 's|XXXXXX/YYYYYY|${{ github.repository }}|g' "$file" || exit 1; done
79-
patch -p1 < builder_repo/patch-attendedsysupgrade.patch
80+
patch -p1 < builder_repo/attendedsysupgrade-with-GitHub.patch
8081
mv -v builder_repo/files ./
8182
chmod -v 755 files/usr/bin/upgrade_custom_openwrt files/www/cgi-bin/github_*
8283
mv -v builder_repo/${{ env.CONFIG_FILE }} .config
Lines changed: 19 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
branch: get_branch(promises[1].release.version),
2323
revision: promises[1].release.revision,
2424
efi: promises[2],
25-
@@ -745,13 +751,271 @@
25+
@@ -745,13 +751,218 @@
2626
E(
2727
'button',
2828
{
@@ -202,78 +202,25 @@
202202
+ }
203203
+
204204
+ if (data.success) {
205-
+ // Firmware is already downloaded to /tmp/firmware.bin
206-
+ // Verify the SHA256 hash if available
207-
+ if (data.sha256) {
208-
+ // Calculate the SHA256 hash of the downloaded file
209-
+ L.resolveDefault(callFileExec('sha256sum', ['/tmp/firmware.bin']), {})
210-
+ .then(result => {
211-
+ if (result && result.stdout) {
212-
+ // Extract the hash from the command output (first 64 characters)
213-
+ const calculatedHash = result.stdout.trim().substring(0, 64);
214-
+
215-
+ if (calculatedHash.toLowerCase() === data.sha256.toLowerCase()) {
216-
+ // Hashes match, proceed with installation
217-
+ ui.showModal(_('Installing...'), [
218-
+ E('div', { class: 'spinning' }, [
219-
+ E('p', _('Installing the sysupgrade image...')),
220-
+ E('p',
221-
+ _('Once the image is written, the system will reboot.')
222-
+ + ' ' +
223-
+ _('This should take at least a minute, so please wait for the login screen.')
224-
+ ),
225-
+ E('b', _('While you are waiting, do not unpower device!')),
226-
+ ]),
227-
+ ]);
228-
+
229-
+ L.resolveDefault(callUpgradeStart(keep), {}).then((response) => {
230-
+ // Wait 10 seconds before we try to reconnect...
231-
+ let hosts = keep ? [] : ['192.168.1.1', 'openwrt.lan'];
232-
+ setTimeout(() => { ui.awaitReconnect(...hosts); }, 10000);
233-
+ });
234-
+ } else {
235-
+ // Hashes don't match, show error
236-
+ ui.showModal(_('Wrong checksum'), [
237-
+ E('p', _('Calculated SHA256: %s').format(calculatedHash)),
238-
+ E('p', _('Expected SHA256: %s').format(data.sha256)),
239-
+ E('p', _('Error during verification of the downloaded firmware. Please try again')),
240-
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
241-
+ ]);
242-
+ }
243-
+ } else {
244-
+ // Could not calculate hash, show error
245-
+ ui.showModal(_('Verification Error'), [
246-
+ E('p', _('Could not calculate SHA256 hash of the downloaded firmware')),
247-
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
248-
+ ]);
249-
+ }
250-
+ })
251-
+ .catch(error => {
252-
+ ui.showModal(_('Verification Error'), [
253-
+ E('p', _('Error calculating SHA256 hash: %s').format(error.message)),
254-
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
255-
+ ]);
256-
+ });
257-
+ } else {
258-
+ // No hash to verify, proceed with installation
259-
+ ui.showModal(_('Installing...'), [
260-
+ E('div', { class: 'spinning' }, [
261-
+ E('p', _('Installing the sysupgrade image...')),
262-
+ E('p',
263-
+ _('Once the image is written, the system will reboot.')
264-
+ + ' ' +
265-
+ _('This should take at least a minute, so please wait for the login screen.')
266-
+ ),
267-
+ E('b', _('While you are waiting, do not unpower device!')),
268-
+ ]),
269-
+ ]);
205+
+ // Firmware is already downloaded to the device and SHA256 verified by the CGI script
206+
+ // Path returned by the CGI script: ${data.path || '/tmp/firmware.bin'}
207+
+ ui.showModal(_('Installing...'), [
208+
+ E('div', { class: 'spinning' }, [
209+
+ E('p', _('Installing the sysupgrade image...')),
210+
+ E('p',
211+
+ _('Once the image is written, the system will reboot.')
212+
+ + ' ' +
213+
+ _('This should take at least a minute, so please wait for the login screen.')
214+
+ ),
215+
+ E('b', _('While you are waiting, do not unpower device!')),
216+
+ ]),
217+
+ ]);
270218
+
271-
+ L.resolveDefault(callUpgradeStart(keep), {}).then((response) => {
272-
+ // Wait 10 seconds before we try to reconnect...
273-
+ let hosts = keep ? [] : ['192.168.1.1', 'openwrt.lan'];
274-
+ setTimeout(() => { ui.awaitReconnect(...hosts); }, 10000);
275-
+ });
276-
+ }
219+
+ L.resolveDefault(callUpgradeStart(keep), {}).then((response) => {
220+
+ // Wait 10 seconds before we try to reconnect...
221+
+ let hosts = keep ? [] : ['192.168.1.1', 'openwrt.lan'];
222+
+ setTimeout(() => { ui.awaitReconnect(...hosts); }, 10000);
223+
+ });
277224
+ } else {
278225
+ ui.showModal(_('Error'), [
279226
+ E('p', _('Unexpected response from firmware download script')),

files/www/cgi-bin/github_fetch

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,58 @@
11
#!/bin/sh
22

33
# CGI header
4-
echo "Content-Type: application/json"
5-
echo ""
4+
printf 'Content-Type: application/json\n\n'
65

7-
# Parse query string to get the tag parameter
6+
# Read query string (from env or first arg)
87
QUERY_STRING="${QUERY_STRING:-$1}"
9-
TAG=$(echo "$QUERY_STRING" | sed -n 's/.*tag=\([^&]*\).*/\1/p' | sed 's/%2B/+/g; s/%3D/=/g; s/%2F/\//g')
108

11-
# If TAG is empty, try to get it directly from the query string
9+
# Extract tag parameter (simple URL-decode of %XX sequences and +->space not needed here)
10+
# Use parameter expansion and sed to get tag value if present, otherwise use whole string
11+
TAG=$(printf '%s' "$QUERY_STRING" | sed -n 's/.*[?&]*tag=\([^&]*\).*/\1/p')
12+
[ -z "$TAG" ] && TAG="$QUERY_STRING"
13+
# Decode percent-encoding (handles %2B, %2F, etc.)
14+
TAG=$(printf '%b' "$(printf '%s' "$TAG" | sed 's/+/ /g;s/%/\\x/g')")
15+
16+
# basic validation
1217
if [ -z "$TAG" ]; then
13-
TAG="$QUERY_STRING"
18+
printf '{"error":"missing tag"}'
19+
exit 1
20+
fi
21+
22+
# Fetch release metadata once
23+
RELEASE_JSON=$(curl -sS "https://api.github.com/repos/XXXXXX/YYYYYY/releases/tags/$TAG") || {
24+
printf '{"error":"failed to fetch release metadata"}'
25+
exit 1
26+
}
27+
28+
# Extract sysupgrade asset URL and sha256sums asset URL and any inline digest
29+
URL=$(printf '%s' "$RELEASE_JSON" | jq -r '.assets[] | select(.browser_download_url|contains("sysupgrade")) | .browser_download_url // empty' | head -n1)
30+
if [ -z "$URL" ]; then
31+
printf '{"error":"No sysupgrade firmware found"}'
32+
exit 1
1433
fi
15-
# Fetch the latest release from GitHub and extract the sysupgrade asset URL
16-
URL=$(curl -sS "https://api.github.com/repos/XXXXXX/YYYYYY/releases/tags/$TAG" | jq -r '.assets[] | select(.browser_download_url | contains("sysupgrade")) | select((length == 0) | not) | .browser_download_url')
17-
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
18-
echo '{"error": "No sysupgrade firmware found"}'
34+
35+
FILENAME=$(basename "$URL")
36+
# Try: 1) sha256sums asset file, 2) asset .digest field (sha256:)
37+
SHA256_URL=$(printf '%s' "$RELEASE_JSON" | jq -r '.assets[] | select(.browser_download_url|contains("sha256sums")) | .browser_download_url // empty' | head -n1)
38+
# download sha256sums and extract matching line
39+
[ -n "$SHA256_URL" ] && SHA256=$(curl -sSL "$SHA256_URL" | sed -n "s|\([0-9a-fA-F]*\).*${FILENAME}|\1|p")
40+
# fallback to metadata if present
41+
[ -z "$SHA256" ] && SHA256=$(printf '%s' "$RELEASE_JSON" | jq -r '.assets[] | select(.browser_download_url|contains("sysupgrade")) | .digest // empty' | sed -n 's/^sha256://p' | head -n1)
42+
43+
if [ -z "$SHA256" ]; then
44+
# If still missing, report and stop
45+
printf '{"error":"Could not get SHA256 hash for %s"}' "$FILENAME"
46+
exit 1
47+
fi
48+
49+
# Download firmware and verify SHA256
50+
FIRMWAREFILE="/tmp/firmware.bin"
51+
if curl -sS -L -o "$FIRMWAREFILE" "$URL" && printf '%s %s\n' "$SHA256" "$FIRMWAREFILE" | sha256sum -cs >/dev/null 2>&1; then
52+
printf '{"success":true,"path":"%s"}' "$FIRMWAREFILE"
53+
exit 0
1954
else
20-
SHA256_URL=$(curl -sS "https://api.github.com/repos/XXXXXX/YYYYYY/releases/tags/$TAG" | jq -r '.assets[] | select(.browser_download_url | contains("sha256sums")) | select((length == 0) | not) | .browser_download_url')
21-
if [ -n "$SHA256_URL" ] && [ "$SHA256_URL" != "null" ]; then
22-
FILENAME=$(basename "$URL")
23-
# Get the SHA256 hash for the specific file
24-
SHA256=$(curl -sSL "$SHA256_URL" | sed -n "s|\(.*\) .*${FILENAME}.*|\1|p")
25-
fi
26-
if [ -z "$SHA256" ]; then
27-
SHA256=$(curl -sS "https://api.github.com/repos/XXXXXX/YYYYYY/releases/tags/$TAG" | jq -r '.assets[] | select(.browser_download_url | contains("sysupgrade")) | select((length == 0) | not) | .digest' | sed 's|sha256:||')
28-
fi
29-
if [ -z "$SHA256" ]; then
30-
echo "{\"error\": \"Could not get SHA256 hash for $FILENAME\"}"
31-
else
32-
# Download the firmware file to /tmp/firmware.bin
33-
if curl -sS -L -o /tmp/firmware.bin "$URL"; then
34-
echo "{\"success\": true, \"path\": \"/tmp/firmware.bin\", \"sha256\": \"$SHA256\"}"
35-
else
36-
echo "{\"error\": \"Failed to download firmware from $URL\"}"
37-
fi
38-
fi
55+
printf '{"error":"SHA256 hash mismatch for downloaded firmware"}'
56+
rm -f "$FIRMWAREFILE"
57+
exit 1
3958
fi

0 commit comments

Comments
 (0)