Verify docs provenance before deploying to firebase

Bug: b/299107409
Change-Id: Iaebf9835836daa58e916eba4f0a51fa5f71be6ae
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/50981
Reviewed-by: Jesse Seales <jseales@google.com>
Commit-Queue: Drew Roen <drewroen@google.com>
(cherry picked from commit 8dcb78290aa171a326bb45d3045c3dce527fc187)
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/51980
Reviewed-by: Ricardo Amador <ricardoamador@google.com>
diff --git a/recipe_modules/adhoc_validation/api.py b/recipe_modules/adhoc_validation/api.py
index b04cb8c..4376317 100644
--- a/recipe_modules/adhoc_validation/api.py
+++ b/recipe_modules/adhoc_validation/api.py
@@ -106,6 +106,12 @@
             # Only deploy to firebase directly if this is master or main.
             if ((self.m.properties.get('git_branch') in ['master', 'main']) or
                 (git_ref == 'refs/heads/stable')):
+              sha = self.m.buildbucket.gitiles_commit.id
+              gcs_location = 'flutter/%s/api_docs.zip' % sha
+              with self.m.step.nest("Verify docs provenance"):
+                self.m.flutter_bcid.download_and_verify_provenance(
+                    'api_docs.zip', 'flutter_infra_release', gcs_location
+                )
               self.m.firebase.deploy_docs(
                   env=env,
                   env_prefixes=env_prefixes,
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/docs.json b/recipe_modules/adhoc_validation/examples/full.expected/docs.json
index d347b0f..2bf6260 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/docs.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/docs.json
@@ -704,6 +704,571 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docs.Verify docs provenance",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://flutter_infra_release/flutter/abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd/api_docs.zip",
+      "[CLEANUP]/verify_tmp_1/api_docs.zip"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.gsutil download api_docs.zip",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://flutter_infra_release/flutter/abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd/api_docs.zip.intoto.jsonl",
+      "[CLEANUP]/verify_tmp_1/api_docs.zip.intoto.jsonl"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.gsutil download api_docs.zip provenance",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "luci-auth",
+      "token",
+      "-scopes",
+      "https://www.googleapis.com/auth/bcid_verify https://www.googleapis.com/auth/cloud-platform",
+      "-lifetime",
+      "3m"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.get access token for default account",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "file_hash",
+      "[CLEANUP]/verify_tmp_1/api_docs.zip"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.Compute file hash",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_TEXT@Hash calculated: 34bfd2a28bb1bdf702e9a76339b4df74356057a3bcedc83064970e941103071c@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[CLEANUP]/verify_tmp_1/api_docs.zip.intoto.jsonl",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.read api_docs.zip provenance",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_END@api_docs.zip.intoto.jsonl@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "Authorization: Bearer extra.secret.token.should.not.be.logged\n",
+      "[CLEANUP]/authorization"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.write authorization",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "{\"resourceToVerify\": \"misc_software://flutter/engine\", \"artifactInfo\": {\"digests\": {\"sha256\": \"34bfd2a28bb1bdf702e9a76339b4df74356057a3bcedc83064970e941103071c\"}, \"attestations\": [\"\"]}}",
+      "[CLEANUP]/request"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.write request",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@request@{\"resourceToVerify\": \"misc_software://flutter/engine\", \"artifactInfo\": {\"digests\": {\"sha256\": \"34bfd2a28bb1bdf702e9a76339b4df74356057a3bcedc83064970e941103071c\"}, \"attestations\": [\"\"]}}@@@",
+      "@@@STEP_LOG_END@request@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "curl",
+      "-H",
+      "@[CLEANUP]/authorization",
+      "-H",
+      "Content-Type: application/json",
+      "-d",
+      "@[CLEANUP]/request",
+      "https://bcidsoftwareverifier-pa.googleapis.com/v1/software-artifact-verification-requests"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.verify api_docs.zip provenance",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@raw_io.output_text@{\"allowed\": true, \"verificationSummary\": \"This artifact is definitely legitimate!\"}@@@",
+      "@@@STEP_LOG_END@raw_io.output_text@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "remove",
+      "[CLEANUP]/authorization"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.delete authorization",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "remove",
+      "[CLEANUP]/request"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.delete request",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "This artifact is definitely legitimate!",
+      "[CLEANUP]/verify_tmp_1/api_docs.zip.vsa.intoto.jsonl"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.write api_docs.zip.vsa.intoto.jsonl",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@api_docs.zip.vsa.intoto.jsonl@This artifact is definitely legitimate!@@@",
+      "@@@STEP_LOG_END@api_docs.zip.vsa.intoto.jsonl@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CLEANUP]/verify_tmp_1/api_docs.zip.vsa.intoto.jsonl",
+      "gs://flutter_infra_release/flutter/abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd/api_docs.zip.vsa.intoto.jsonl"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "12345abcde12345abcde12345abcde12345abcde",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "flutter:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Docs.Verify docs provenance.gsutil upload \"flutter/abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd/api_docs.zip.vsa.intoto.jsonl\"",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/flutter_infra_release/flutter/abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd/api_docs.zip.vsa.intoto.jsonl@@@"
+    ]
+  },
+  {
     "cmd": [
       "luci-auth",
       "token",
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/linux.json b/recipe_modules/adhoc_validation/examples/full.expected/linux.json
index f7e4420..713772f 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/linux.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/linux.json
@@ -314,6 +314,13 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docs.Verify docs provenance",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "luci-auth",
       "token",
diff --git a/recipe_modules/adhoc_validation/examples/full.py b/recipe_modules/adhoc_validation/examples/full.py
index 4daec50..a7c1362 100644
--- a/recipe_modules/adhoc_validation/examples/full.py
+++ b/recipe_modules/adhoc_validation/examples/full.py
@@ -51,6 +51,7 @@
       api.expect_exception('AssertionError'),
       api.repo_util.flutter_environment_data(checkout_path)
   )
+  fake_bcid_response_success = '{"allowed": true, "verificationSummary": "This artifact is definitely legitimate!"}'
   yield api.test(
       'docs',
       api.platform.name('linux'),
@@ -70,4 +71,8 @@
           revision='abcd' * 10,
           build_number=123,
       ),
+      api.step_data(
+          'Docs.Verify docs provenance.verify api_docs.zip provenance',
+          stdout=api.raw_io.output_text(fake_bcid_response_success)
+      )
   )
diff --git a/recipe_modules/firebase/__init__.py b/recipe_modules/firebase/__init__.py
index 2911537..6651847 100644
--- a/recipe_modules/firebase/__init__.py
+++ b/recipe_modules/firebase/__init__.py
@@ -1,4 +1,5 @@
 DEPS = [
+    'flutter/flutter_bcid',
     'recipe_engine/context',
     'recipe_engine/file',
     'recipe_engine/path',
diff --git a/recipe_modules/flutter_bcid/api.py b/recipe_modules/flutter_bcid/api.py
index 5ef5836..3ba797f 100644
--- a/recipe_modules/flutter_bcid/api.py
+++ b/recipe_modules/flutter_bcid/api.py
@@ -39,9 +39,10 @@
   def upload_provenance(self, local_artifact_path, remote_artifact_path):
     """Generate provenance for given artifact.
 
-    This function acts on one specific local file and one specific
-    remote file location. It does not accept glob patterns or
-    directories.
+    This function acts on one specific local file and one specific remote file
+    location. It does not accept glob patterns or directories. This method is a
+    noop if it is not an official build, as only official builds generate
+    provenance.
 
     parmeters:
       local_artifact_path: (str) path and filename of a specific file.
@@ -57,7 +58,9 @@
     """Downloads and verifies provenance for a specified artifact.
 
     This method downloads an artifact and associated provenance from GCS,
-    verifies it. If verification fails, an error is raised.
+    verifies it. If verification fails, an error is raised. This method is a
+    noop if it is not an official build, as only official builds generate
+    provenance.
 
     parameters:
       filename: (str) the name of the file, eg: "flutter_artifact.zip"
@@ -65,22 +68,23 @@
       gcs_path_without_bucket: (str) the GCS path, excluding gs://{bucket}/
         eg: "flutter/004d0bdf6721bc65cdb9a558908b2de4cfac97c5/sky_engine.zip"
     """
-    verify_temp_path = self.m.path.mkdtemp("verify")
-    download_path = download_path = verify_temp_path.join(filename)
-    bcid_response = self.m.dart.download_and_verify(
-        filename, bucket, gcs_path_without_bucket, download_path,
-        'misc_software://flutter/engine'
-    )
+    if self.is_official_build():
+      verify_temp_path = self.m.path.mkdtemp("verify")
+      download_path = download_path = verify_temp_path.join(filename)
+      bcid_response = self.m.dart.download_and_verify(
+          filename, bucket, gcs_path_without_bucket, download_path,
+          'misc_software://flutter/engine'
+      )
 
-    artifact_vsa = bcid_response['verificationSummary']
-    vsa_local_path = f'{download_path}{VSA_EXTENSION}'
-    self.m.file.write_text(
-        f'write {filename}{VSA_EXTENSION}', vsa_local_path, artifact_vsa
-    )
-    vsa_path_without_bucket = gcs_path_without_bucket + VSA_EXTENSION
-    self.m.gsutil.upload(
-        vsa_local_path,
-        bucket,
-        vsa_path_without_bucket,
-        name=f'upload "{vsa_path_without_bucket}"'
-    )
+      artifact_vsa = bcid_response['verificationSummary']
+      vsa_local_path = f'{download_path}{VSA_EXTENSION}'
+      self.m.file.write_text(
+          f'write {filename}{VSA_EXTENSION}', vsa_local_path, artifact_vsa
+      )
+      vsa_path_without_bucket = gcs_path_without_bucket + VSA_EXTENSION
+      self.m.gsutil.upload(
+          vsa_local_path,
+          bucket,
+          vsa_path_without_bucket,
+          name=f'upload "{vsa_path_without_bucket}"',
+      )