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 android.os.incremental; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.ParcelFileDescriptor; 22 23 import java.io.ByteArrayInputStream; 24 import java.io.ByteArrayOutputStream; 25 import java.io.EOFException; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.OutputStream; 29 import java.nio.ByteBuffer; 30 import java.nio.ByteOrder; 31 32 /** 33 * V4 signature fields. 34 * Keep in sync with APKSig authoritative copy. 35 * @hide 36 */ 37 public class V4Signature { 38 public static final String EXT = ".idsig"; 39 public static final int SUPPORTED_VERSION = 2; 40 41 public static final int HASHING_ALGORITHM_SHA256 = 1; 42 public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12; 43 44 public static final int INCFS_MAX_SIGNATURE_SIZE = 8096; // incrementalfs.h 45 46 /** 47 * IncFS hashing data. 48 */ 49 public static class HashingInfo { 50 public final int hashAlgorithm; // only 1 == SHA256 supported 51 public final byte log2BlockSize; // only 12 (block size 4096) supported now 52 @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max 53 @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page 54 HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash)55 HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) { 56 this.hashAlgorithm = hashAlgorithm; 57 this.log2BlockSize = log2BlockSize; 58 this.salt = salt; 59 this.rawRootHash = rawRootHash; 60 } 61 62 /** 63 * Constructs HashingInfo from byte array. 64 */ 65 @NonNull fromByteArray(@onNull byte[] bytes)66 public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException { 67 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 68 final int hashAlgorithm = buffer.getInt(); 69 final byte log2BlockSize = buffer.get(); 70 byte[] salt = readBytes(buffer); 71 byte[] rawRootHash = readBytes(buffer); 72 return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash); 73 } 74 } 75 76 /** 77 * V4 signature data. 78 */ 79 public static class SigningInfo { 80 public final byte[] apkDigest; // used to match with the corresponding APK 81 public final byte[] certificate; // ASN.1 DER form 82 public final byte[] additionalData; // a free-form binary data blob 83 public final byte[] publicKey; // ASN.1 DER, must match the certificate 84 public final int signatureAlgorithmId; // see the APK v2 doc for the list 85 public final byte[] signature; 86 SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, byte[] publicKey, int signatureAlgorithmId, byte[] signature)87 SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, 88 byte[] publicKey, int signatureAlgorithmId, byte[] signature) { 89 this.apkDigest = apkDigest; 90 this.certificate = certificate; 91 this.additionalData = additionalData; 92 this.publicKey = publicKey; 93 this.signatureAlgorithmId = signatureAlgorithmId; 94 this.signature = signature; 95 } 96 97 /** 98 * Constructs SigningInfo from byte array. 99 */ fromByteArray(byte[] bytes)100 public static SigningInfo fromByteArray(byte[] bytes) throws IOException { 101 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 102 byte[] apkDigest = readBytes(buffer); 103 byte[] certificate = readBytes(buffer); 104 byte[] additionalData = readBytes(buffer); 105 byte[] publicKey = readBytes(buffer); 106 int signatureAlgorithmId = buffer.getInt(); 107 byte[] signature = readBytes(buffer); 108 return new SigningInfo(apkDigest, certificate, additionalData, publicKey, 109 signatureAlgorithmId, signature); 110 } 111 } 112 113 public final int version; // Always 2 for now. 114 /** 115 * Raw byte array containing the IncFS hashing data. 116 * @see HashingInfo#fromByteArray(byte[]) 117 */ 118 @Nullable public final byte[] hashingInfo; 119 120 /** 121 * Raw byte array containing the V4 signature data. 122 * <p>Passed as-is to the kernel. Can be retrieved later. 123 * @see SigningInfo#fromByteArray(byte[]) 124 */ 125 @Nullable public final byte[] signingInfo; 126 127 /** 128 * Construct a V4Signature from .idsig file. 129 */ readFrom(ParcelFileDescriptor pfd)130 public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException { 131 try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) { 132 return readFrom(stream); 133 } 134 } 135 136 /** 137 * Construct a V4Signature from a byte array. 138 */ 139 @NonNull readFrom(@onNull byte[] bytes)140 public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException { 141 try (InputStream stream = new ByteArrayInputStream(bytes)) { 142 return readFrom(stream); 143 } 144 } 145 146 /** 147 * Store the V4Signature to a byte-array. 148 */ toByteArray()149 public byte[] toByteArray() { 150 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { 151 this.writeTo(stream); 152 return stream.toByteArray(); 153 } catch (IOException e) { 154 return null; 155 } 156 } 157 158 /** 159 * Combines necessary data to a signed data blob. 160 * The blob can be validated against signingInfo.signature. 161 * 162 * @param fileSize - size of the signed file (APK) 163 */ getSignedData(long fileSize, HashingInfo hashingInfo, SigningInfo signingInfo)164 public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo, 165 SigningInfo signingInfo) { 166 final int size = 167 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( 168 hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize( 169 signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize( 170 signingInfo.additionalData); 171 ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); 172 buffer.putInt(size); 173 buffer.putLong(fileSize); 174 buffer.putInt(hashingInfo.hashAlgorithm); 175 buffer.put(hashingInfo.log2BlockSize); 176 writeBytes(buffer, hashingInfo.salt); 177 writeBytes(buffer, hashingInfo.rawRootHash); 178 writeBytes(buffer, signingInfo.apkDigest); 179 writeBytes(buffer, signingInfo.certificate); 180 writeBytes(buffer, signingInfo.additionalData); 181 return buffer.array(); 182 } 183 isVersionSupported()184 public boolean isVersionSupported() { 185 return this.version == SUPPORTED_VERSION; 186 } 187 V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo)188 private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) { 189 this.version = version; 190 this.hashingInfo = hashingInfo; 191 this.signingInfo = signingInfo; 192 } 193 readFrom(InputStream stream)194 private static V4Signature readFrom(InputStream stream) throws IOException { 195 final int version = readIntLE(stream); 196 int maxSize = INCFS_MAX_SIGNATURE_SIZE; 197 final byte[] hashingInfo = readBytes(stream, maxSize); 198 if (hashingInfo != null) { 199 maxSize -= hashingInfo.length; 200 } 201 final byte[] signingInfo = readBytes(stream, maxSize); 202 return new V4Signature(version, hashingInfo, signingInfo); 203 } 204 writeTo(OutputStream stream)205 private void writeTo(OutputStream stream) throws IOException { 206 writeIntLE(stream, this.version); 207 writeBytes(stream, this.hashingInfo); 208 writeBytes(stream, this.signingInfo); 209 } 210 211 // Utility methods. bytesSize(byte[] bytes)212 private static int bytesSize(byte[] bytes) { 213 return 4/*length*/ + (bytes == null ? 0 : bytes.length); 214 } 215 readFully(InputStream stream, byte[] buffer)216 private static void readFully(InputStream stream, byte[] buffer) throws IOException { 217 int len = buffer.length; 218 int n = 0; 219 while (n < len) { 220 int count = stream.read(buffer, n, len - n); 221 if (count < 0) { 222 throw new EOFException(); 223 } 224 n += count; 225 } 226 } 227 readIntLE(InputStream stream)228 private static int readIntLE(InputStream stream) throws IOException { 229 final byte[] buffer = new byte[4]; 230 readFully(stream, buffer); 231 return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); 232 } 233 writeIntLE(OutputStream stream, int v)234 private static void writeIntLE(OutputStream stream, int v) throws IOException { 235 final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt( 236 v).array(); 237 stream.write(buffer); 238 } 239 readBytes(InputStream stream, int maxSize)240 private static byte[] readBytes(InputStream stream, int maxSize) throws IOException { 241 try { 242 final int size = readIntLE(stream); 243 if (size > maxSize) { 244 throw new IOException( 245 "Signature is too long. Max allowed is " + INCFS_MAX_SIGNATURE_SIZE); 246 } 247 final byte[] bytes = new byte[size]; 248 readFully(stream, bytes); 249 return bytes; 250 } catch (EOFException ignored) { 251 return null; 252 } 253 } 254 readBytes(ByteBuffer buffer)255 private static byte[] readBytes(ByteBuffer buffer) throws IOException { 256 if (buffer.remaining() < 4) { 257 throw new EOFException(); 258 } 259 final int size = buffer.getInt(); 260 if (buffer.remaining() < size) { 261 throw new EOFException(); 262 } 263 final byte[] bytes = new byte[size]; 264 buffer.get(bytes); 265 return bytes; 266 } 267 writeBytes(OutputStream stream, byte[] bytes)268 private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException { 269 if (bytes == null) { 270 writeIntLE(stream, 0); 271 return; 272 } 273 writeIntLE(stream, bytes.length); 274 stream.write(bytes); 275 } 276 writeBytes(ByteBuffer buffer, byte[] bytes)277 private static void writeBytes(ByteBuffer buffer, byte[] bytes) { 278 if (bytes == null) { 279 buffer.putInt(0); 280 return; 281 } 282 buffer.putInt(bytes.length); 283 buffer.put(bytes); 284 } 285 } 286