From 1c1928319283413571a235bbb96f98e090f4d7a6 Mon Sep 17 00:00:00 2001 From: Me7c7 Date: Fri, 30 Aug 2024 10:53:26 +0300 Subject: [PATCH] Huawei: Improved error handling for bin file parsing. --- .../devices/huawei/HuaweiBinAppParser.java | 71 +++++++++++++------ .../devices/huawei/HuaweiInstallHandler.java | 8 +-- .../devices/huawei/HuaweiFwHelper.java | 23 +++--- 3 files changed, 65 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBinAppParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBinAppParser.java index 1a31773f1..46b619fce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBinAppParser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBinAppParser.java @@ -7,15 +7,23 @@ import java.util.Arrays; public class HuaweiBinAppParser { + public static class HuaweiBinAppParseError extends Exception { + public HuaweiBinAppParseError(String str) { + super(str); + } + } + public static class AppFileEntry { public String filename; public String path; public byte[] content; } - private static final int signHeaderLen = 32; - private static final byte[] signMagic = "hw signed app ".getBytes(); - private static final byte[] signVersion = "1000".getBytes(); + private static final byte BIN_APP_MAGIC = (byte) 0xbe; + + private static final int SIGN_HEADER_LEN = 32; + private static final byte[] SIGN_MAGIC = "hw signed app ".getBytes(); + private static final byte[] SIGN_VERSION = "1000".getBytes(); private String packageName; @@ -26,59 +34,76 @@ public class HuaweiBinAppParser { } public byte[] getEntryContent(String filename) { - for(AppFileEntry en: entries) { - if(en.filename.equals(filename)) { + for (AppFileEntry en : entries) { + if (en.filename.equals(filename)) { return en.content; } } return null; } - private byte[] readData(ByteBuffer data) { + private byte[] readData(ByteBuffer data) throws Exception { long len = data.getLong(); - byte[] newPayload = new byte[(int)len]; - data.get(newPayload, 0, (int)len); + if (len > Integer.MAX_VALUE || len <= 0) { + throw new HuaweiBinAppParseError("Invalid data length"); + } + byte[] newPayload = new byte[(int) len]; + data.get(newPayload, 0, (int) len); return newPayload; } - private String readString(ByteBuffer data) { - int len = data.getInt(); + private String readStringInternal(ByteBuffer data, int len) throws Exception { byte[] newPayload = new byte[len]; data.get(newPayload, 0, len); return new String(newPayload, StandardCharsets.UTF_8); } + + private String readString(ByteBuffer data) throws Exception { + int len = data.getInt(); + if (len <= 0 || len > data.remaining()) { + throw new HuaweiBinAppParseError("Invalid string length"); + } + return readStringInternal(data, len); + } + + private String readEmptyString(ByteBuffer data) throws Exception { + int len = data.getInt(); + if (len < 0 || len > data.remaining()) { + throw new HuaweiBinAppParseError("Invalid string length"); + } + return readStringInternal(data, len); + } + private int getSignLen(byte[] in) { - byte[] signatureHeader = Arrays.copyOfRange(in, in.length - signHeaderLen, in.length); + byte[] signatureHeader = Arrays.copyOfRange(in, in.length - SIGN_HEADER_LEN, in.length); ByteBuffer signatureHeaderBuf = ByteBuffer.wrap(signatureHeader); - byte[] magic = new byte[signMagic.length]; - signatureHeaderBuf.get(magic, 0, signMagic.length); - if(!Arrays.equals(signMagic, magic)) + byte[] magic = new byte[SIGN_MAGIC.length]; + signatureHeaderBuf.get(magic, 0, SIGN_MAGIC.length); + if (!Arrays.equals(SIGN_MAGIC, magic)) return 0; - byte[] version = new byte[signVersion.length]; - signatureHeaderBuf.get(version, 0, signVersion.length); - if(!Arrays.equals(signVersion, version)) + byte[] version = new byte[SIGN_VERSION.length]; + signatureHeaderBuf.get(version, 0, SIGN_VERSION.length); + if (!Arrays.equals(SIGN_VERSION, version)) return 0; //NOTE: we need only signature size. return signatureHeaderBuf.getInt(); } - - public void parseData(byte[] in) throws Exception { //NOTE: Binary app file signed. We should avoid to read this signature. int signLen = getSignLen(in); ByteBuffer data = ByteBuffer.wrap(in); byte magic = data.get(); - if(magic != (byte)0xbe){ - throw new Exception("Invalid magic"); + if (magic != BIN_APP_MAGIC) { + throw new HuaweiBinAppParseError("Invalid magic"); } this.packageName = readString(data); - while(data.remaining() > signLen) { + while (data.remaining() > signLen) { AppFileEntry ent = new AppFileEntry(); ent.filename = readString(data); - ent.path = readString(data); + ent.path = readEmptyString(data); ent.content = readData(data); entries.add(ent); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java index 46fe0bde5..3aafb654f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java @@ -72,8 +72,8 @@ public class HuaweiInstallHandler implements InstallHandler { GenericItem installItem = new GenericItem(); - if (helper.getWatchfacePreviewBitmap() != null) { - installItem.setPreview(helper.getWatchfacePreviewBitmap()); + if (helper.getPreviewBitmap() != null) { + installItem.setPreview(helper.getPreviewBitmap()); } installItem.setName(description.title); @@ -116,8 +116,8 @@ public class HuaweiInstallHandler implements InstallHandler { GenericItem installItem = new GenericItem(); - if (helper.getWatchfacePreviewBitmap() != null) { - installItem.setPreview(helper.getWatchfacePreviewBitmap()); + if (helper.getPreviewBitmap() != null) { + installItem.setPreview(helper.getPreviewBitmap()); } installItem.setName(config.bundleName); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java index 646d643eb..4263dd0cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java @@ -48,7 +48,7 @@ public class HuaweiFwHelper { private byte fileType = 0; String fileName = ""; - Bitmap watchfacePreviewBitmap; + Bitmap previewBitmap; HuaweiWatchfaceManager.WatchfaceDescription watchfaceDescription; HuaweiAppManager.AppConfig appConfig; Context mContext; @@ -83,7 +83,6 @@ public class HuaweiFwHelper { try { final UriHelper uriHelper = UriHelper.get(uri, this.mContext); - InputStream inputStream = uriHelper.openInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); @@ -96,10 +95,12 @@ public class HuaweiFwHelper { } buffer.flush(); - byte[] hap_data = buffer.toByteArray(); + byte[] appData = buffer.toByteArray(); + + inputStream.close(); HuaweiBinAppParser app = new HuaweiBinAppParser(); - app.parseData(hap_data); + app.parseData(appData); byte[] config = app.getEntryContent("config.json"); if(config == null) @@ -107,20 +108,22 @@ public class HuaweiFwHelper { appConfig = new HuaweiAppManager.AppConfig(new String(config)); fileName = app.getPackageName() + "_INSTALL"; //TODO: INSTALL or UPDATE suffix - fw = hap_data; + fw = appData; fileSize = fw.length; byte[] icon = app.getEntryContent("icon_small.png"); if(icon != null) { - watchfacePreviewBitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length); + previewBitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length); } return true; } catch (FileNotFoundException e) { - LOG.error("The watchface file was not found.", e); + LOG.error("The app file was not found.", e); } catch (IOException e) { LOG.error("General IO error occurred.", e); + } catch (HuaweiBinAppParser.HuaweiBinAppParseError e) { + LOG.error("Error parsing app File", e); } catch (Exception e) { LOG.error("Unknown error occurred.", e); } @@ -164,7 +167,7 @@ public class HuaweiFwHelper { watchfaceDescription = new HuaweiWatchfaceManager.WatchfaceDescription(xmlDescription); if (watchfacePackage.fileExists("preview/cover.jpg")) { final byte[] preview = watchfacePackage.getFileFromZip("preview/cover.jpg"); - watchfacePreviewBitmap = BitmapFactory.decodeByteArray(preview, 0, preview.length); + previewBitmap = BitmapFactory.decodeByteArray(preview, 0, preview.length); } byte[] watchfaceZip = watchfacePackage.getFileFromZip("com.huawei.watchface"); @@ -203,8 +206,8 @@ public class HuaweiFwHelper { return isWatchface() || isAPP(); } - public Bitmap getWatchfacePreviewBitmap() { - return watchfacePreviewBitmap; + public Bitmap getPreviewBitmap() { + return previewBitmap; } public HuaweiWatchfaceManager.WatchfaceDescription getWatchfaceDescription() {