All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.os.incremental.V4Signature Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os.incremental;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ParcelFileDescriptor;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * V4 signature fields.
 * Keep in sync with APKSig authoritative copy.
 * @hide
 */
public class V4Signature {
    public static final String EXT = ".idsig";
    public static final int SUPPORTED_VERSION = 2;

    public static final int HASHING_ALGORITHM_SHA256 = 1;
    public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;

    public static final int INCFS_MAX_SIGNATURE_SIZE = 8096;  // incrementalfs.h

    /**
     * IncFS hashing data.
     */
    public static class HashingInfo {
        public final int hashAlgorithm; // only 1 == SHA256 supported
        public final byte log2BlockSize; // only 12 (block size 4096) supported now
        @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
        @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page

        HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
            this.hashAlgorithm = hashAlgorithm;
            this.log2BlockSize = log2BlockSize;
            this.salt = salt;
            this.rawRootHash = rawRootHash;
        }

        /**
         * Constructs HashingInfo from byte array.
         */
        @NonNull
        public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
            ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
            final int hashAlgorithm = buffer.getInt();
            final byte log2BlockSize = buffer.get();
            byte[] salt = readBytes(buffer);
            byte[] rawRootHash = readBytes(buffer);
            return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
        }
    }

    /**
     * V4 signature data.
     */
    public static class SigningInfo {
        public final byte[] apkDigest;  // used to match with the corresponding APK
        public final byte[] certificate; // ASN.1 DER form
        public final byte[] additionalData; // a free-form binary data blob
        public final byte[] publicKey; // ASN.1 DER, must match the certificate
        public final int signatureAlgorithmId; // see the APK v2 doc for the list
        public final byte[] signature;

        SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
                byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
            this.apkDigest = apkDigest;
            this.certificate = certificate;
            this.additionalData = additionalData;
            this.publicKey = publicKey;
            this.signatureAlgorithmId = signatureAlgorithmId;
            this.signature = signature;
        }

        /**
         * Constructs SigningInfo from byte array.
         */
        public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
            ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
            byte[] apkDigest = readBytes(buffer);
            byte[] certificate = readBytes(buffer);
            byte[] additionalData = readBytes(buffer);
            byte[] publicKey = readBytes(buffer);
            int signatureAlgorithmId = buffer.getInt();
            byte[] signature = readBytes(buffer);
            return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
                    signatureAlgorithmId, signature);
        }
    }

    public final int version; // Always 2 for now.
    /**
     * Raw byte array containing the IncFS hashing data.
     * @see HashingInfo#fromByteArray(byte[])
     */
    @Nullable public final byte[] hashingInfo;

    /**
     * Raw byte array containing the V4 signature data.
     * 

Passed as-is to the kernel. Can be retrieved later. * @see SigningInfo#fromByteArray(byte[]) */ @Nullable public final byte[] signingInfo; /** * Construct a V4Signature from .idsig file. */ public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException { try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) { return readFrom(stream); } } /** * Construct a V4Signature from a byte array. */ @NonNull public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException { try (InputStream stream = new ByteArrayInputStream(bytes)) { return readFrom(stream); } } /** * Store the V4Signature to a byte-array. */ public byte[] toByteArray() { try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { this.writeTo(stream); return stream.toByteArray(); } catch (IOException e) { return null; } } /** * Combines necessary data to a signed data blob. * The blob can be validated against signingInfo.signature. * * @param fileSize - size of the signed file (APK) */ public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo, SigningInfo signingInfo) { final int size = 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize( signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.additionalData); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(size); buffer.putLong(fileSize); buffer.putInt(hashingInfo.hashAlgorithm); buffer.put(hashingInfo.log2BlockSize); writeBytes(buffer, hashingInfo.salt); writeBytes(buffer, hashingInfo.rawRootHash); writeBytes(buffer, signingInfo.apkDigest); writeBytes(buffer, signingInfo.certificate); writeBytes(buffer, signingInfo.additionalData); return buffer.array(); } public boolean isVersionSupported() { return this.version == SUPPORTED_VERSION; } private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) { this.version = version; this.hashingInfo = hashingInfo; this.signingInfo = signingInfo; } private static V4Signature readFrom(InputStream stream) throws IOException { final int version = readIntLE(stream); int maxSize = INCFS_MAX_SIGNATURE_SIZE; final byte[] hashingInfo = readBytes(stream, maxSize); if (hashingInfo != null) { maxSize -= hashingInfo.length; } final byte[] signingInfo = readBytes(stream, maxSize); return new V4Signature(version, hashingInfo, signingInfo); } private void writeTo(OutputStream stream) throws IOException { writeIntLE(stream, this.version); writeBytes(stream, this.hashingInfo); writeBytes(stream, this.signingInfo); } // Utility methods. private static int bytesSize(byte[] bytes) { return 4/*length*/ + (bytes == null ? 0 : bytes.length); } private static void readFully(InputStream stream, byte[] buffer) throws IOException { int len = buffer.length; int n = 0; while (n < len) { int count = stream.read(buffer, n, len - n); if (count < 0) { throw new EOFException(); } n += count; } } private static int readIntLE(InputStream stream) throws IOException { final byte[] buffer = new byte[4]; readFully(stream, buffer); return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); } private static void writeIntLE(OutputStream stream, int v) throws IOException { final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt( v).array(); stream.write(buffer); } private static byte[] readBytes(InputStream stream, int maxSize) throws IOException { try { final int size = readIntLE(stream); if (size > maxSize) { throw new IOException( "Signature is too long. Max allowed is " + INCFS_MAX_SIGNATURE_SIZE); } final byte[] bytes = new byte[size]; readFully(stream, bytes); return bytes; } catch (EOFException ignored) { return null; } } private static byte[] readBytes(ByteBuffer buffer) throws IOException { if (buffer.remaining() < 4) { throw new EOFException(); } final int size = buffer.getInt(); if (buffer.remaining() < size) { throw new EOFException(); } final byte[] bytes = new byte[size]; buffer.get(bytes); return bytes; } private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException { if (bytes == null) { writeIntLE(stream, 0); return; } writeIntLE(stream, bytes.length); stream.write(bytes); } private static void writeBytes(ByteBuffer buffer, byte[] bytes) { if (bytes == null) { buffer.putInt(0); return; } buffer.putInt(bytes.length); buffer.put(bytes); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy