1945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz/* 2945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * Copyright 2016, The Android Open Source Project 3945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * 4945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * Licensed under the Apache License, Version 2.0 (the "License"); 5945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * you may not use this file except in compliance with the License. 6945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * You may obtain a copy of the License at 7945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * 8945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * http://www.apache.org/licenses/LICENSE-2.0 9945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * 10945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * Unless required by applicable law or agreed to in writing, software 11945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * distributed under the License is distributed on an "AS IS" BASIS, 12945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * See the License for the specific language governing permissions and 14945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * limitations under the License. 15945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz */ 16945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 17945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzpackage com.android.managedprovisioning.task; 18945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 19945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport static com.android.internal.util.Preconditions.checkNotNull; 20945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 21945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport android.content.Context; 22945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport android.content.pm.PackageInfo; 23945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport android.content.pm.PackageManager; 24945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport android.content.pm.Signature; 25945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport android.text.TextUtils; 26945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 27945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport com.android.internal.annotations.VisibleForTesting; 28f7a9eea8fe577f2f5edbbe6e73891a54351286c6Benjamin Franzimport com.android.managedprovisioning.common.ProvisionLogger; 29945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport com.android.managedprovisioning.R; 30945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport com.android.managedprovisioning.common.StoreUtils; 31945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport com.android.managedprovisioning.common.Utils; 32945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport com.android.managedprovisioning.model.PackageDownloadInfo; 33945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport com.android.managedprovisioning.model.ProvisioningParams; 34945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 35945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport java.util.Arrays; 36945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport java.util.LinkedList; 37945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzimport java.util.List; 38945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 39945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz/** 40945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * Verifies the management app apk downloaded previously in {@link DownloadPackageTask}. 41945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * 42945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * <p>The first check verifies that a {@link android.app.admin.DeviceAdminReceiver} is present in 43945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * the apk and that it corresponds to the one provided via 44945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * {@link ProvisioningParams#deviceAdminComponentName}.</p> 45945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * 46945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * <p>The second check verifies that the package or signature checksum matches the ones given via 47945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * {@link PackageDownloadInfo#packageChecksum} or {@link PackageDownloadInfo#signatureChecksum} 48945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * respectively. The package checksum takes priority in case both are present.</p> 49945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz */ 50945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franzpublic class VerifyPackageTask extends AbstractProvisioningTask { 51945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz public static final int ERROR_HASH_MISMATCH = 0; 52945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz public static final int ERROR_DEVICE_ADMIN_MISSING = 1; 53945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 54945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private final Utils mUtils; 55945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private final DownloadPackageTask mDownloadPackageTask; 56945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private final PackageManager mPackageManager; 57945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private final PackageDownloadInfo mDownloadInfo; 58945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 59945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz public VerifyPackageTask( 60945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz DownloadPackageTask downloadPackageTask, 61945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz Context context, 62945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisioningParams params, 63945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz Callback callback) { 64945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz this(new Utils(), downloadPackageTask, context, params, callback); 65945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 66945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 67945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz @VisibleForTesting 68945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz VerifyPackageTask( 69945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz Utils utils, 70945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz DownloadPackageTask downloadPackageTask, 71945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz Context context, 72945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisioningParams params, 73945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz Callback callback) { 74945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz super(context, params, callback); 75945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 76945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz mUtils = checkNotNull(utils); 77945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz mDownloadPackageTask = checkNotNull(downloadPackageTask); 78945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz mPackageManager = mContext.getPackageManager(); 79945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz mDownloadInfo = checkNotNull(params.deviceAdminDownloadInfo); 80945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 81945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 82945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz @Override 83945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz public void run(int userId) { 84945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz final String downloadLocation = mDownloadPackageTask.getDownloadedPackageLocation(); 85945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (TextUtils.isEmpty(downloadLocation)) { 86945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.logw("VerifyPackageTask invoked, but download location is null"); 87945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz success(); 88945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return; 89945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 90945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 91945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz PackageInfo packageInfo = mPackageManager.getPackageArchiveInfo(downloadLocation, 92945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz PackageManager.GET_SIGNATURES | PackageManager.GET_RECEIVERS); 93be71433cf40426a420be70cd6388b198403e423dVictor Chang String packageName = mProvisioningParams.inferDeviceAdminPackageName(); 94be71433cf40426a420be70cd6388b198403e423dVictor Chang // Device admin package name can't be null 95be71433cf40426a420be70cd6388b198403e423dVictor Chang if (packageInfo == null || packageName == null) { 96be71433cf40426a420be70cd6388b198403e423dVictor Chang ProvisionLogger.loge("Device admin package info or name is null"); 97be71433cf40426a420be70cd6388b198403e423dVictor Chang error(ERROR_DEVICE_ADMIN_MISSING); 98be71433cf40426a420be70cd6388b198403e423dVictor Chang return; 99be71433cf40426a420be70cd6388b198403e423dVictor Chang } 100be71433cf40426a420be70cd6388b198403e423dVictor Chang 101be71433cf40426a420be70cd6388b198403e423dVictor Chang if (mUtils.findDeviceAdminInPackageInfo(packageName, 102be71433cf40426a420be70cd6388b198403e423dVictor Chang mProvisioningParams.deviceAdminComponentName, packageInfo) == null) { 103945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz error(ERROR_DEVICE_ADMIN_MISSING); 104945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return; 105945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 106945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 107945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (mDownloadInfo.packageChecksum.length > 0) { 108945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (!doesPackageHashMatch(downloadLocation, mDownloadInfo.packageChecksum, 109945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz mDownloadInfo.packageChecksumSupportsSha1)) { 110945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz error(ERROR_HASH_MISMATCH); 111945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return; 112945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 113945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } else { 114945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (!doesASignatureHashMatch(packageInfo, mDownloadInfo.signatureChecksum)) { 115945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz error(ERROR_HASH_MISMATCH); 116945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return; 117945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 118945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 119945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 120945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz success(); 121945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 122945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 123945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz @Override 124945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz public int getStatusMsgId() { 125945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return R.string.progress_install; 126945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 127945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 128945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private List<byte[]> computeHashesOfAllSignatures(Signature[] signatures) { 129945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (signatures == null) { 130945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return null; 131945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 132945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 133945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz List<byte[]> hashes = new LinkedList<>(); 134945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz for (Signature signature : signatures) { 135945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz byte[] hash = mUtils.computeHashOfByteArray(signature.toByteArray()); 136945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz hashes.add(hash); 137945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 138945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return hashes; 139945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 140945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 141945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private boolean doesASignatureHashMatch(PackageInfo packageInfo, byte[] signatureChecksum) { 142945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz // Check whether a signature hash of downloaded apk matches the hash given in constructor. 143945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.logd("Checking " + Utils.SHA256_TYPE 144945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz + "-hashes of all signatures of downloaded package."); 145945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz List<byte[]> sigHashes = computeHashesOfAllSignatures(packageInfo.signatures); 146945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (sigHashes == null || sigHashes.isEmpty()) { 147945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("Downloaded package does not have any signatures."); 148945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return false; 149945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 150945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 151945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz for (byte[] sigHash : sigHashes) { 152945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (Arrays.equals(sigHash, signatureChecksum)) { 153945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return true; 154945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 155945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 156945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 157945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("Provided hash does not match any signature hash."); 158945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("Hash provided by programmer: " 159945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz + StoreUtils.byteArrayToString(signatureChecksum)); 160945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("Hashes computed from package signatures: "); 161945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz for (byte[] sigHash : sigHashes) { 162945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (sigHash != null) { 163945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge(StoreUtils.byteArrayToString(sigHash)); 164945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 165945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 166945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 167945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return false; 168945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 169945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 170945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz /** 171945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * Check whether package hash of downloaded file matches the hash given in PackageDownloadInfo. 172945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * By default, SHA-256 is used to verify the file hash. 173945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * If mPackageDownloadInfo.packageChecksumSupportsSha1 == true, SHA-1 hash is also supported for 174945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz * backwards compatibility. 175945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz */ 176945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz private boolean doesPackageHashMatch(String downloadLocation, byte[] packageChecksum, 177945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz boolean supportsSha1) { 178945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz byte[] packageSha256Hash, packageSha1Hash = null; 179945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 180945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.logd("Checking file hash of entire apk file."); 181945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz packageSha256Hash = mUtils.computeHashOfFile(downloadLocation, Utils.SHA256_TYPE); 182945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (Arrays.equals(packageChecksum, packageSha256Hash)) { 183945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return true; 184945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 185945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 186945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz // Fall back to SHA-1 187945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (supportsSha1) { 188945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz packageSha1Hash = mUtils.computeHashOfFile(downloadLocation, Utils.SHA1_TYPE); 189945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (Arrays.equals(packageChecksum, packageSha1Hash)) { 190945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return true; 191945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 192945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 193945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz 194945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("Provided hash does not match file hash."); 195945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("Hash provided by programmer: " 196945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz + StoreUtils.byteArrayToString(packageChecksum)); 197945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (packageSha256Hash != null) { 198945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("SHA-256 Hash computed from file: " 199945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz + StoreUtils.byteArrayToString(packageSha256Hash)); 200945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 201945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz if (packageSha1Hash != null) { 202945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz ProvisionLogger.loge("SHA-1 Hash computed from file: " 203945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz + StoreUtils.byteArrayToString(packageSha1Hash)); 204945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 205945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz return false; 206945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz } 207945e609e967c33897df9e5f53b86e0eaa6e53e2eBenjamin Franz} 208