1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.core.appdigest; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY; 20 21 import androidx.annotation.IntDef; 22 import androidx.annotation.RestrictTo; 23 import androidx.core.util.Preconditions; 24 25 import org.jspecify.annotations.NonNull; 26 import org.jspecify.annotations.Nullable; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.InputStream; 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.security.cert.Certificate; 33 import java.security.cert.CertificateEncodingException; 34 import java.security.cert.CertificateException; 35 import java.security.cert.CertificateFactory; 36 import java.security.cert.X509Certificate; 37 38 /** 39 * A typed checksum of an APK. 40 * 41 * @see Checksums#getChecksums 42 */ 43 public final class Checksum { 44 /** 45 * Root SHA256 hash of a 4K Merkle tree computed over all file bytes. 46 * <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>. 47 * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>. 48 * 49 * Recommended for all new applications. 50 * Can be used by kernel to enforce authenticity and integrity of the APK. 51 * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst#">See fs-verity for details</a> 52 * 53 * @see Checksums#getChecksums 54 */ 55 public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001; 56 57 /** 58 * MD5 hash computed over all file bytes. 59 * 60 * @see Checksums#getChecksums 61 * @deprecated Not platform enforced. Cryptographically broken and unsuitable for further use. 62 * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. 63 * Provided for completeness' sake and to support legacy usecases. 64 */ 65 @Deprecated 66 public static final int TYPE_WHOLE_MD5 = 0x00000002; 67 68 /** 69 * SHA1 hash computed over all file bytes. 70 * 71 * @see Checksums#getChecksums 72 * @deprecated Not platform enforced. Broken and should not be used. 73 * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. 74 * Provided for completeness' sake and to support legacy usecases. 75 */ 76 @Deprecated 77 public static final int TYPE_WHOLE_SHA1 = 0x00000004; 78 79 /** 80 * SHA256 hash computed over all file bytes. 81 * @deprecated Not platform enforced. 82 * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. 83 * Provided for completeness' sake and to support legacy usecases. 84 * 85 * @see Checksums#getChecksums 86 */ 87 @Deprecated 88 public static final int TYPE_WHOLE_SHA256 = 0x00000008; 89 90 /** 91 * SHA512 hash computed over all file bytes. 92 * @deprecated Not platform enforced. 93 * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. 94 * Provided for completeness' sake and to support legacy usecases. 95 * 96 * @see Checksums#getChecksums 97 */ 98 @Deprecated 99 public static final int TYPE_WHOLE_SHA512 = 0x00000010; 100 101 /** 102 * Root SHA256 hash of a 1M Merkle tree computed over protected content. 103 * Excludes signing block. 104 * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>. 105 * 106 * @see Checksums#getChecksums 107 */ 108 public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020; 109 110 /** 111 * Root SHA512 hash of a 1M Merkle tree computed over protected content. 112 * Excludes signing block. 113 * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>. 114 * 115 * @see Checksums#getChecksums 116 */ 117 public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040; 118 119 @RestrictTo(LIBRARY) 120 @IntDef(flag = true, value = { 121 TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, 122 TYPE_WHOLE_MD5, 123 TYPE_WHOLE_SHA1, 124 TYPE_WHOLE_SHA256, 125 TYPE_WHOLE_SHA512, 126 TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, 127 TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, 128 }) 129 @Retention(RetentionPolicy.SOURCE) 130 public @interface Type {} 131 132 /** 133 * Checksum for which split. Null indicates base.apk. 134 */ 135 private final @Nullable String mSplitName; 136 /** 137 * Checksum type. 138 */ 139 private final @Checksum.Type int mType; 140 /** 141 * Checksum value. 142 */ 143 private final byte @NonNull [] mValue; 144 /** 145 * For Installer-provided checksums, package name of the Installer. 146 */ 147 private final @Nullable String mInstallerPackageName; 148 /** 149 * For Installer-provided checksums, certificate of the Installer. 150 */ 151 private final byte @Nullable [] mInstallerCertificate; 152 153 /** 154 * Constructor, internal use only. 155 */ Checksum(@ullable String splitName, @Checksum.Type int type, byte @NonNull [] value)156 Checksum(@Nullable String splitName, @Checksum.Type int type, 157 byte @NonNull [] value) { 158 this(splitName, type, value, (String) null, (byte[]) null); 159 } 160 161 /** 162 * Constructor, internal use only. 163 */ Checksum(@ullable String splitName, @Checksum.Type int type, byte @NonNull [] value, @Nullable String installerPackageName, @Nullable Certificate installerCertificate)164 Checksum(@Nullable String splitName, @Checksum.Type int type, byte @NonNull [] value, 165 @Nullable String installerPackageName, @Nullable Certificate installerCertificate) 166 throws CertificateEncodingException { 167 this(splitName, type, value, installerPackageName, 168 (installerCertificate != null) ? installerCertificate.getEncoded() : null); 169 } 170 171 /** 172 * Creates a new Checksum. 173 * 174 * @param splitName 175 * Checksum for which split. Null indicates base.apk. 176 * @param type 177 * Checksum type. 178 * @param value 179 * Checksum value. 180 * @param installerPackageName 181 * For Installer-provided checksums, package name of the Installer. 182 * @param installerCertificate 183 * For Installer-provided checksums, certificate of the Installer. 184 */ Checksum( @ullable String splitName, @Type int type, byte @NonNull [] value, @Nullable String installerPackageName, byte @Nullable [] installerCertificate)185 Checksum( 186 @Nullable String splitName, 187 @Type int type, 188 byte @NonNull [] value, 189 @Nullable String installerPackageName, 190 byte @Nullable [] installerCertificate) { 191 Preconditions.checkNotNull(value); 192 this.mSplitName = splitName; 193 this.mType = type; 194 this.mValue = value; 195 this.mInstallerPackageName = installerPackageName; 196 this.mInstallerCertificate = installerCertificate; 197 } 198 199 /** 200 * Checksum for which split. Null indicates base.apk. 201 */ getSplitName()202 public @Nullable String getSplitName() { 203 return mSplitName; 204 } 205 206 /** 207 * For Installer-provided checksums, package name of the Installer. 208 */ getInstallerPackageName()209 public @Nullable String getInstallerPackageName() { 210 return mInstallerPackageName; 211 } 212 213 /** 214 * Checksum type. 215 */ getType()216 public @Checksum.Type int getType() { 217 return mType; 218 } 219 220 /** 221 * Checksum value. 222 */ getValue()223 public byte @NonNull [] getValue() { 224 return mValue; 225 } 226 227 /** 228 * For Installer-provided checksums, certificate of the Installer. 229 * @throws CertificateException in case when certificate can't be re-created from serialized 230 * data. 231 */ getInstallerCertificate()232 public @Nullable Certificate getInstallerCertificate() throws CertificateException { 233 if (mInstallerCertificate == null) { 234 return null; 235 } 236 final CertificateFactory cf = CertificateFactory.getInstance("X.509"); 237 final InputStream is = new ByteArrayInputStream(mInstallerCertificate); 238 final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); 239 return cert; 240 } 241 } 242