Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8561a459 authored by Alex Klyubin's avatar Alex Klyubin Committed by android-build-merger
Browse files

Merge "APK Signature Scheme v2 signing logic for apksigner-core." am:...

Merge "APK Signature Scheme v2 signing logic for apksigner-core." am: 2627b419 am: c65bc9ea am: 8d894eda
am: af2d4dee

* commit 'af2d4dee':
  APK Signature Scheme v2 signing logic for apksigner-core.

Change-Id: I68f96efddc2dc30e2f771254644e3c5272df0f6c
parents d676b5c6 af2d4dee
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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 com.android.apksigner.core.internal.apk.v2;

/**
 * APK Signature Scheme v2 content digest algorithm.
 */
enum ContentDigestAlgorithm {
    /** SHA2-256 over 1 MB chunks. */
    CHUNKED_SHA256("SHA-256", 256 / 8),

    /** SHA2-512 over 1 MB chunks. */
    CHUNKED_SHA512("SHA-512", 512 / 8);

    private final String mJcaMessageDigestAlgorithm;
    private final int mChunkDigestOutputSizeBytes;

    private ContentDigestAlgorithm(
            String jcaMessageDigestAlgorithm, int chunkDigestOutputSizeBytes) {
        mJcaMessageDigestAlgorithm = jcaMessageDigestAlgorithm;
        mChunkDigestOutputSizeBytes = chunkDigestOutputSizeBytes;
    }

    /**
     * Returns the {@link java.security.MessageDigest} algorithm used for computing digests of
     * chunks by this content digest algorithm.
     */
    String getJcaMessageDigestAlgorithm() {
        return mJcaMessageDigestAlgorithm;
    }

    /**
     * Returns the size (in bytes) of the digest of a chunk of content.
     */
    int getChunkDigestOutputSizeBytes() {
        return mChunkDigestOutputSizeBytes;
    }
}
 No newline at end of file
+52 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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 com.android.apksigner.core.internal.apk.v2;

import com.android.apksigner.core.util.DataSink;

import java.nio.ByteBuffer;
import java.security.MessageDigest;

/**
 * Data sink which feeds all received data into the associated {@link MessageDigest} instances. Each
 * {@code MessageDigest} instance receives the same data.
 */
class MessageDigestSink implements DataSink {

    private final MessageDigest[] mMessageDigests;

    MessageDigestSink(MessageDigest[] digests) {
        mMessageDigests = digests;
    }

    @Override
    public void consume(byte[] buf, int offset, int length) {
        for (MessageDigest md : mMessageDigests) {
            md.update(buf, offset, length);
        }
    }

    @Override
    public void consume(ByteBuffer buf) {
        int originalPosition = buf.position();
        for (MessageDigest md : mMessageDigests) {
            // Reset the position back to the original because the previous iteration's
            // MessageDigest.update set the buffer's position to the buffer's limit.
            buf.position(originalPosition);
            md.update(buf);
        }
    }
}
+142 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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 com.android.apksigner.core.internal.apk.v2;

import com.android.apksigner.core.internal.util.Pair;

import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;

/**
 * APK Signature Scheme v2 content digest algorithm.
 */
public enum SignatureAlgorithm {
    /**
     * RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc, content
     * digested using SHA2-256 in 1 MB chunks.
     */
    RSA_PSS_WITH_SHA256(
            0x0101,
            ContentDigestAlgorithm.CHUNKED_SHA256,
            "RSA",
            Pair.of("SHA256withRSA/PSS",
                    new PSSParameterSpec(
                            "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1))),

    /**
     * RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc, content
     * digested using SHA2-512 in 1 MB chunks.
     */
    RSA_PSS_WITH_SHA512(
            0x0102,
            ContentDigestAlgorithm.CHUNKED_SHA512,
            "RSA",
            Pair.of(
                    "SHA512withRSA/PSS",
                    new PSSParameterSpec(
                            "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1))),

    /** RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */
    RSA_PKCS1_V1_5_WITH_SHA256(
            0x0103,
            ContentDigestAlgorithm.CHUNKED_SHA256,
            "RSA",
            Pair.of("SHA256withRSA", null)),

    /** RSASSA-PKCS1-v1_5 with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks. */
    RSA_PKCS1_V1_5_WITH_SHA512(
            0x0104,
            ContentDigestAlgorithm.CHUNKED_SHA512,
            "RSA",
            Pair.of("SHA512withRSA", null)),

    /** ECDSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */
    ECDSA_WITH_SHA256(
            0x0201,
            ContentDigestAlgorithm.CHUNKED_SHA256,
            "EC",
            Pair.of("SHA256withECDSA", null)),

    /** ECDSA with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks. */
    ECDSA_WITH_SHA512(
            0x0202,
            ContentDigestAlgorithm.CHUNKED_SHA512,
            "EC",
            Pair.of("SHA512withECDSA", null)),

    /** DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */
    DSA_WITH_SHA256(
            0x0301,
            ContentDigestAlgorithm.CHUNKED_SHA256,
            "DSA",
            Pair.of("SHA256withDSA", null));

    private final int mId;
    private final String mJcaKeyAlgorithm;
    private final ContentDigestAlgorithm mContentDigestAlgorithm;
    private final Pair<String, ? extends AlgorithmParameterSpec> mJcaSignatureAlgAndParams;

    private SignatureAlgorithm(int id,
            ContentDigestAlgorithm contentDigestAlgorithm,
            String jcaKeyAlgorithm,
            Pair<String, ? extends AlgorithmParameterSpec> jcaSignatureAlgAndParams) {
        mId = id;
        mContentDigestAlgorithm = contentDigestAlgorithm;
        mJcaKeyAlgorithm = jcaKeyAlgorithm;
        mJcaSignatureAlgAndParams = jcaSignatureAlgAndParams;
    }

    /**
     * Returns the ID of this signature algorithm as used in APK Signature Scheme v2 wire format.
     */
    int getId() {
        return mId;
    }

    /**
     * Returns the content digest algorithm associated with this signature algorithm.
     */
    ContentDigestAlgorithm getContentDigestAlgorithm() {
        return mContentDigestAlgorithm;
    }

    /**
     * Returns the JCA {@link java.security.Key} algorithm used by this signature scheme.
     */
    String getJcaKeyAlgorithm() {
        return mJcaKeyAlgorithm;
    }

    /**
     * Returns the {@link java.security.Signature} algorithm and the {@link AlgorithmParameterSpec}
     * (or null if not needed) to parameterize the {@code Signature}.
     */
    Pair<String, ? extends AlgorithmParameterSpec> getJcaSignatureAlgorithmAndParams() {
        return mJcaSignatureAlgAndParams;
    }

    static SignatureAlgorithm findById(int id) {
        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
            if (alg.getId() == id) {
                return alg;
            }
        }

        return null;
    }
}
+614 −0

File added.

Preview size limit exceeded, changes collapsed.

+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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 com.android.apksigner.core.internal.util;

import com.android.apksigner.core.util.DataSink;
import com.android.apksigner.core.util.DataSource;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * {@link DataSource} backed by a {@link ByteBuffer}.
 */
public class ByteBufferDataSource implements DataSource {

    private final ByteBuffer mBuffer;
    private final long mSize;

    /**
     * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided
     * buffer between the buffer's position and limit.
     */
    public ByteBufferDataSource(ByteBuffer buffer) {
        mBuffer = buffer.slice();
        mSize = buffer.remaining();
    }

    @Override
    public long size() {
        return mSize;
    }

    @Override
    public void feed(long offset, int size, DataSink sink) throws IOException {
        if (offset < 0) {
            throw new IllegalArgumentException("offset: " + offset);
        }
        if (size < 0) {
            throw new IllegalArgumentException("size: " + size);
        }
        if (offset > mSize) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") > source size (" + mSize + ")");
        }
        long endOffset = offset + size;
        if (endOffset < offset) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") + size (" + size + ") overflow");
        }
        if (endOffset > mSize) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") + size (" + size + ") > source size (" + mSize  +")");
        }

        int chunkPosition = (int) offset; // safe to downcast because mSize <= Integer.MAX_VALUE
        int chunkLimit = (int) endOffset; // safe to downcast because mSize <= Integer.MAX_VALUE
        ByteBuffer chunk;
        // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position
        // and limit fields, to be more specific). We thus use synchronization around these
        // state-changing operations to make instances of this class thread-safe.
        synchronized (mBuffer) {
            // ByteBuffer.limit(int) and .position(int) check that that the position >= limit
            // invariant is not broken. Thus, the only way to safely change position and limit
            // without caring about their current values is to first set position to 0 or set the
            // limit to capacity.
            mBuffer.position(0);

            mBuffer.limit(chunkLimit);
            mBuffer.position(chunkPosition);
            chunk = mBuffer.slice();
        }

        sink.consume(chunk);
    }
}
Loading