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

Commit 70349b46 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Ports part of the backup/encryption/chunking code from gmscore."

parents 9f9c47db 477a17e6
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.server.backup.encryption.chunk;

import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.EXPLICIT_STARTS;
import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.INLINE_LENGTHS;

import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** IntDef corresponding to the ChunkOrderingType enum in the ChunksMetadataProto protobuf. */
@IntDef({CHUNK_ORDERING_TYPE_UNSPECIFIED, EXPLICIT_STARTS, INLINE_LENGTHS})
@Retention(RetentionPolicy.SOURCE)
public @interface ChunkOrderingType {}
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.server.backup.encryption.chunking;

import java.io.IOException;

/** Writes backup data either as a diff script or as raw data, determined by the implementation. */
public interface BackupWriter {
    /** Writes the given bytes to the output. */
    void writeBytes(byte[] bytes) throws IOException;

    /**
     * Writes an existing chunk from the previous backup to the output.
     *
     * <p>Note: not all implementations support this method.
     */
    void writeChunk(long start, int length) throws IOException;

    /** Returns the number of bytes written, included bytes copied from the old file. */
    long getBytesWritten();

    /**
     * Indicates that no more bytes or chunks will be written.
     *
     * <p>After calling this, you may not call {@link #writeBytes(byte[])} or {@link
     * #writeChunk(long, int)}
     */
    void flush() throws IOException;
}
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.server.backup.encryption.chunking;

import com.android.internal.util.Preconditions;
import com.android.server.backup.encryption.chunk.ChunkHash;
import java.util.Arrays;
import java.util.Objects;

/**
 * A chunk of a file encrypted using AES/GCM.
 *
 * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename
 * encryptedBytes(), key() and nonce().
 */
public class EncryptedChunk {
    public static final int KEY_LENGTH_BYTES = ChunkHash.HASH_LENGTH_BYTES;
    public static final int NONCE_LENGTH_BYTES = 12;

    /**
     * Constructs a new instance with the given key, nonce, and encrypted bytes.
     *
     * @param key SHA-256 Hmac of the chunk plaintext.
     * @param nonce Nonce with which the bytes of the chunk were encrypted.
     * @param encryptedBytes Encrypted bytes of the chunk.
     */
    public static EncryptedChunk create(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
        Preconditions.checkArgument(
                nonce.length == NONCE_LENGTH_BYTES, "Nonce does not have the correct length.");
        return new EncryptedChunk(key, nonce, encryptedBytes);
    }

    private ChunkHash mKey;
    private byte[] mNonce;
    private byte[] mEncryptedBytes;

    private EncryptedChunk(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
        mKey = key;
        mNonce = nonce;
        mEncryptedBytes = encryptedBytes;
    }

    /** The SHA-256 Hmac of the plaintext bytes of the chunk. */
    public ChunkHash key() {
        return mKey;
    }

    /** The nonce with which the chunk was encrypted. */
    public byte[] nonce() {
        return mNonce;
    }

    /** The encrypted bytes of the chunk. */
    public byte[] encryptedBytes() {
        return mEncryptedBytes;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof EncryptedChunk)) {
            return false;
        }

        EncryptedChunk encryptedChunkOrdering = (EncryptedChunk) o;
        return Arrays.equals(mEncryptedBytes, encryptedChunkOrdering.mEncryptedBytes)
                && Arrays.equals(mNonce, encryptedChunkOrdering.mNonce)
                && mKey.equals(encryptedChunkOrdering.mKey);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mKey, Arrays.hashCode(mNonce), Arrays.hashCode(mEncryptedBytes));
    }
}
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.server.backup.encryption.chunking;

import com.android.server.backup.encryption.chunk.ChunkOrderingType;
import java.io.IOException;

/** Encodes an {@link EncryptedChunk} as bytes to write to the encrypted backup file. */
public interface EncryptedChunkEncoder {
    /**
     * Encodes the given chunk and asks the writer to write it.
     *
     * <p>The chunk will be encoded in the format [nonce]+[encrypted data].
     *
     * <p>TODO(b/116575321): Choose a more descriptive method name after the code move is done.
     */
    void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException;

    /**
     * Returns the length in bytes that this chunk would be if encoded with {@link
     * #writeChunkToWriter}.
     */
    int getEncodedLengthOfChunk(EncryptedChunk chunk);

    /**
     * Returns the {@link ChunkOrderingType} that must be included in the backup file, when using
     * this decoder, so that the file may be correctly decoded.
     */
    @ChunkOrderingType
    int getChunkOrderingType();
}
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.server.backup.encryption.chunking;

import com.android.server.backup.encryption.chunk.ChunkOrderingType;
import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
import java.io.IOException;

/**
 * Encodes an {@link EncryptedChunk} as bytes, prepending the length of the chunk.
 *
 * <p>This allows us to decode the backup file during restore without any extra information about
 * the boundaries of the chunks. The backup file should contain a chunk ordering in mode {@link
 * ChunksMetadataProto#INLINE_LENGTHS}.
 *
 * <p>We use this implementation during key value backup.
 */
public class InlineLengthsEncryptedChunkEncoder implements EncryptedChunkEncoder {
    public static final int BYTES_LENGTH = Integer.SIZE / Byte.SIZE;

    private final LengthlessEncryptedChunkEncoder mLengthlessEncryptedChunkEncoder =
            new LengthlessEncryptedChunkEncoder();

    @Override
    public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
        int length = mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
        writer.writeBytes(toByteArray(length));
        mLengthlessEncryptedChunkEncoder.writeChunkToWriter(writer, chunk);
    }

    @Override
    public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
        return BYTES_LENGTH + mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
    }

    @Override
    @ChunkOrderingType
    public int getChunkOrderingType() {
        return ChunksMetadataProto.INLINE_LENGTHS;
    }

    /**
     * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to
     * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code
     * 0x12131415} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15}}.
     *
     * <p>Equivalent to guava's Ints.toByteArray.
     */
    static byte[] toByteArray(int value) {
        return new byte[] {
            (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value
        };
    }
}
Loading