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