diff --git a/modules/apps/prebuilt.nix b/modules/apps/prebuilt.nix index 4fa1dc9f..24c5a2e2 100644 --- a/modules/apps/prebuilt.nix +++ b/modules/apps/prebuilt.nix @@ -27,21 +27,6 @@ let include $(BUILD_PREBUILT) ''); - build-tools = - (pkgs.androidPkgs.sdk (p: with p.stable; [ tools build-tools-30-0-2 ])) - + "/share/android-sdk/build-tools/30.0.2"; - - apksigner = pkgs.runCommand "apksigner" { nativeBuildInputs = [ pkgs.makeWrapper ]; } '' - mkdir -p $out/bin - makeWrapper "${pkgs.jre8_headless}/bin/java" "$out/bin/apksigner" \ - --add-flags "-jar ${build-tools}/lib/apksigner.jar" - ''; - - signApk = {name, apk, keyPath}: pkgs.runCommand "${name}-signed.apk" { nativeBuildInputs = [ pkgs.jre8_headless ]; } '' - cp ${apk} $out - ${apksigner}/bin/apksigner sign --key ${keyPath}.pk8 --cert ${keyPath}.x509.pem $out - ''; - # TODO: Uses IFD. Try to avoid using this. apkFingerprint = apk: (import pkgs.runCommand "apk-fingerprint" { nativeBuildInputs = [ pkgs.jre8_headless ]; } '' fingerprint=$(keytool -printcert -jarfile ${apk} | grep "SHA256:" | tr --delete ':' | cut --delimiter ' ' --fields 3) @@ -139,8 +124,8 @@ in # Uses the sandbox exception in /keys signedApk = mkDefault ( - if config.certificate == "PRESIGNED" then config.apk else (signApk { - inherit (config) name apk; + if config.certificate == "PRESIGNED" then config.apk else (pkgs.signApk { + inherit (config) apk; keyPath = _config.build.sandboxKeyPath config.certificate; })); @@ -172,7 +157,7 @@ in ### Check minSdkVersion, targetSdkVersion # TODO: Also check permissions? - MANIFEST_DUMP=$(${build-tools}/aapt2 d xmltree --file AndroidManifest.xml ${apk}) + MANIFEST_DUMP=$(${pkgs.build-tools}/aapt2 d xmltree --file AndroidManifest.xml ${apk}) # It would be better if we could convert it back into true XML and then select based on XPath MIN_SDK_VERSION=$(echo "$MANIFEST_DUMP" | grep minSdkVersion | cut -d= -f2) diff --git a/modules/google.nix b/modules/google.nix index d47e39ef..c8244f78 100644 --- a/modules/google.nix +++ b/modules/google.nix @@ -66,7 +66,10 @@ in config_priorityOnlyDndExemptPackages = [ "com.google.android.dialer" ]; # Found under PixelConfigOverlayCommon.apk }; apps.prebuilt.GoogleDialer = { - apk = "${productPath}/priv-app/GoogleDialer/GoogleDialer.apk"; + apk = pkgs.verifyApk { + apk = "${productPath}/priv-app/GoogleDialer/GoogleDialer.apk"; + sha256 = "e2d049f3a01192f620b1240615fa8c13badc553c22bc6fddfca45c84d8fc545d"; + }; privileged = true; certificate = "PRESIGNED"; }; @@ -77,40 +80,58 @@ in google.base.enable = true; google.dialer.enable = true; apps.prebuilt = { - Tycho = { - apk = "${productPath}/app/Tycho/Tycho.apk"; # Google Fi app + Tycho = { # Google Fi app + apk = pkgs.verifyApk { + apk = "${productPath}/app/Tycho/Tycho.apk"; + sha256 = "4c36af4a5bdad97c1f3d8b283416d244496c2ac5eafe8226079ef6f676fd1859"; + }; certificate = "PRESIGNED"; }; - GCS = { - apk = "${productPath}/priv-app/GCS/GCS.apk"; # Google Connectivity Services (does wifi VPN at least) + GCS = { # Google Connectivity Services (does wifi VPN at least) + apk = pkgs.verifyApk { + apk = "${productPath}/priv-app/GCS/GCS.apk"; + sha256 = "8efed9b84a6320eafde625cea7bb6bae0e320473d0e3c04fb0cd43b779078e1d"; + }; certificate = "PRESIGNED"; privileged = true; }; #### Disabling for now, since calls aren't working #### # CarrierServices = { -# apk = "${productPath}/priv-app/CarrierServices/CarrierServices.apk"; # Google Carrier Services. com.google.android.ims (needed for wifi calls) +# apk = pkgs.verifyApk { +# apk = "${productPath}/priv-app/CarrierServices/CarrierServices.apk"; # Google Carrier Services. com.google.android.ims (needed for wifi calls) +# sha256 = "c25d5afacb6783109d6136d79353fad4f6541c3545d25228a18703d043ca783f"; +# }; # certificate = "PRESIGNED"; # privileged = true; # }; - CarrierSettings = { - apk = "${productPath}/priv-app/CarrierSettings/CarrierSettings.apk"; # com.google.android.carrier + CarrierSettings = { # com.google.android.carrier + apk = pkgs.verifyApk { + apk = "${productPath}/priv-app/CarrierSettings/CarrierSettings.apk"; + sha256 = "383d1e1b525ec6fb8204c5bf9b8390d37fd157e78b8b7212d61487b178066d20"; + }; certificate = "PRESIGNED"; privileged = true; }; - CarrierSetup = { - apk = "${systemExtPath}/priv-app/CarrierSetup/CarrierSetup.apk"; # com.google.android.carriersetup + CarrierSetup = { # com.google.android.carriersetup + apk = "${systemExtPath}/priv-app/CarrierSetup/CarrierSetup.apk"; # Uses device-specific keys certificate = "PRESIGNED"; privileged = true; }; } // (optionalAttrs (config.deviceFamily == "crosshatch") { # TODO: Generalize to other devices with esim EuiccGoogle = { - apk = "${productPath}/priv-app/EuiccGoogle/EuiccGoogle.apk"; + apk = pkgs.verifyApk { + apk = "${productPath}/priv-app/EuiccGoogle/EuiccGoogle.apk"; + sha256 = "7e26b6d5802a16799448ad635868f0345d6730310634684c0ae44e7e9f7ea764"; + }; certificate = "PRESIGNED"; privileged = true; }; }) // (optionalAttrs ((config.deviceFamily == "crosshatch") && (config.androidVersion >= 10)) { EuiccSupportPixel = { - apk = "${productPath}/priv-app/EuiccSupportPixel/EuiccSupportPixel.apk"; + apk = pkgs.verifyApk { + apk = "${productPath}/priv-app/EuiccSupportPixel/EuiccSupportPixel.apk"; + sha256 = "e0afeca77af15aee48a25ead314c576f8f274682a8fba3365610878f7c1ddb6b"; + }; certificate = "PRESIGNED"; privileged = true; }; diff --git a/modules/microg.nix b/modules/microg.nix index 53766ee3..889747b6 100644 --- a/modules/microg.nix +++ b/modules/microg.nix @@ -4,6 +4,10 @@ with lib; let version = "0.2.13.203915"; + verifyApk = apk: pkgs.verifyApk { + inherit apk; + sha256 = "9bd06727e62796c0130eb6dab39b73157451582cbd138e86c468acc395d14165"; # O=NOGAPPS Project, C=DE + }; in { options = { @@ -27,10 +31,10 @@ in # Used https://github.com/lineageos4microg/android_prebuilts_prebuiltapks as source for Android.mk options apps.prebuilt = { GmsCore = { - apk = pkgs.fetchurl { + apk = verifyApk (pkgs.fetchurl { url = "https://github.com/microg/android_packages_apps_GmsCore/releases/download/v${version}/GmsCore-v${version}.apk"; sha256 = "0266zbmf20z7sa4xbgkq6f3qglrf8kcl24p6d87bzygm72340wxa"; - }; + }); packageName = "com.google.android.gms"; privileged = true; privappPermissions = [ "FAKE_PACKAGE_SIGNATURE" "INSTALL_LOCATION_PROVIDER" "CHANGE_DEVICE_IDLE_TEMP_WHITELIST" "UPDATE_APP_OPS_STATS" ]; @@ -38,16 +42,16 @@ in allowInPowerSave = true; }; - GsfProxy.apk = pkgs.fetchurl { + GsfProxy.apk = verifyApk (pkgs.fetchurl { url = "https://github.com/microg/android_packages_apps_GsfProxy/releases/download/v0.1.0/GsfProxy.apk"; sha256 = "14ln6i1qg435x223x3vndd608mra19d58yqqhhf6mw018cbip2c6"; - }; + }); FakeStore = { - apk = pkgs.fetchurl { + apk = verifyApk (pkgs.fetchurl { url = "https://github.com/microg/android_packages_apps_FakeStore/releases/download/v0.1.0/FakeStore-v0.1.0.apk"; sha256 = "1kp5v4qajp4cdx8pxw6j4776bcwc9f8jgfpiyllpk1kbhq92w1ci"; - }; + }); packageName = "com.android.vending"; privileged = true; privappPermissions = [ "FAKE_PACKAGE_SIGNATURE" ]; diff --git a/pkgs/build-tools/default.nix b/pkgs/build-tools/default.nix new file mode 100644 index 00000000..d174db59 --- /dev/null +++ b/pkgs/build-tools/default.nix @@ -0,0 +1,42 @@ +{ lib, runCommand, androidPkgs, makeWrapper, jre8_headless }: + +let + # getName snippet originally from nixpkgs/pkgs/build-support/trivial-builders.nix + getName = fname: apk: + if lib.elem (builtins.typeOf apk) [ "path" "string" ] + then lib.removeSuffix ".apk" (builtins.baseNameOf apk) + else + if builtins.isAttrs apk && builtins.hasAttr "name" apk + then lib.removeSuffix ".apk" apk.name + else throw "${fname}: please supply a `name` argument because a default name can only be computed when the `apk` is a path or is an attribute set with a `name` attribute."; + + build-tools = + (androidPkgs.sdk (p: with p.stable; [ tools build-tools-30-0-2 ])) + + "/share/android-sdk/build-tools/30.0.2"; + + apksigner = runCommand "apksigner" { nativeBuildInputs = [ makeWrapper ]; } '' + mkdir -p $out/bin + makeWrapper "${jre8_headless}/bin/java" "$out/bin/apksigner" \ + --add-flags "-jar ${build-tools}/lib/apksigner.jar" + ''; + + signApk = { apk, keyPath, name ? (getName "signApk" apk) + "-signed.apk" }: runCommand name {} '' + cp ${apk} $out + ${apksigner}/bin/apksigner sign --key ${keyPath}.pk8 --cert ${keyPath}.x509.pem $out + ''; + + # Currently only supports 1 signer. + verifyApk = { apk, sha256, name ? (getName "verifyApk" apk) + ".apk" }: runCommand name {} '' + sha256=$(${apksigner}/bin/apksigner verify --print-certs ${apk} | grep "^Signer #1 certificate SHA-256 digest: " | cut -d" " -f6 || exit 1) + + if [[ "$sha256" = "${sha256}" ]]; then + echo "${name} APK certificate digest matches ${sha256}" + ln -s ${apk} $out + else + echo "${name} APK certificate digest $sha256 is not ${sha256}" + exit 1 + fi + ''; +in { + inherit build-tools apksigner signApk verifyApk; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index 2c82c028..116a180d 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -43,6 +43,14 @@ let fetchgerritpatchset = super.callPackage ./fetchgerritpatchset {}; nix-prefetch-git = super.callPackage ./nix-prefetch-git {}; + + ### + + inherit (super.callPackage ./build-tools {}) + build-tools + apksigner + signApk + verifyApk; }; in import nixpkgs (args // {