1//
2// Copyright (C) 2017 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
17package com.android.verifiedboot.storage;
18
19import javacard.framework.CardRuntimeException;
20import javacard.framework.Util;
21
22import com.android.verifiedboot.storage.BackupInterface;
23import com.android.verifiedboot.globalstate.owner.OwnerInterface;
24
25class VersionStorage implements BackupInterface {
26    final public static byte NUM_SLOTS = (byte) 8;
27    final public static byte SLOT_BYTES = (byte) 8;
28    final private static byte EXPORT_VERSION = (byte)0x01;
29    private OwnerInterface globalState;
30    private byte[] storage;
31
32    public VersionStorage(OwnerInterface globalStateRef) {
33      storage = new byte[NUM_SLOTS * SLOT_BYTES];
34      globalState = globalStateRef;
35      Util.arrayFillNonAtomic(storage, (short) 0, (short) storage.length, (byte) 0x00);
36    }
37
38    /**
39     * Copies content from the given slot in |out| and returns true.
40     *
41     * @param slot slot number to retrieve
42     * @param out array to copy the slot data to.
43     * @param oOffset offset into |out| to start the copy at.
44     * @return 0x0 on success and an error otherwise.
45     */
46    public short getSlot(byte slot, byte[] out, short oOffset) {
47        if (slot > NUM_SLOTS - 1) {
48            return 0x0001;
49        }
50        try {
51            Util.arrayCopy(storage, (short)(SLOT_BYTES * slot),
52                         out, oOffset, SLOT_BYTES);
53        } catch (CardRuntimeException e) {
54            return 0x0002;
55        }
56        return 0x0;
57    }
58
59    /**
60     * Copies content for the given slot from |in| and returns true.
61     *
62     * @param slot slot number to retrieve
63     * @param in array to copy the slot data from.
64     * @param iOffset into |in| to start the copy at.
65     * @return 0x0 on success or an error code.
66     */
67    public short setSlot(byte slot, byte[] in, short iOffset) {
68        if (slot > NUM_SLOTS - 1) {
69            return 0x0001;
70        }
71        // Slots can be set only if we're in the bootloader
72        // or we're not yet in production.
73        if (globalState.production() == true &&
74            globalState.inBootloader() == false) {
75            return 0x0003;
76        }
77        try {
78            Util.arrayCopy(in, iOffset,
79                     storage, (short)(SLOT_BYTES * slot), SLOT_BYTES);
80        } catch (CardRuntimeException e) {
81            return 0x0002;
82        }
83        return 0;
84    }
85
86    /**
87     * {@inheritDoc}
88     *
89     * Checks the size and version prefix prior to copying.
90     */
91    @Override
92    public boolean restore(byte[] inBytes, short inBytesOffset, short inBytesLength) {
93        if (inBytesLength == (short) 0 ||
94            inBytesLength > (short)(storage.length + 1)) {
95            return false;
96        }
97        if (inBytes[0] != EXPORT_VERSION) {
98            return false;
99        }
100        try {
101            Util.arrayCopy(inBytes, inBytesOffset, storage, (short) 0, inBytesLength);
102        } catch (CardRuntimeException e) {
103            return false;
104        }
105        return true;
106    }
107
108    /**
109     * {@inheritDoc}
110     *
111     * Copies storage to outBytes with a leading version byte which can be
112     * checked on restore.
113     */
114    @Override
115    public short backup(byte[] outBytes, short outBytesOffset) {
116        try {
117            // Tag the export version that way if any internal storage format changes
118            // occur, they can be handled.
119            outBytes[(short)(outBytesOffset + 1)] = EXPORT_VERSION;
120            Util.arrayCopy(storage, (short) 0, outBytes, (short)(outBytesOffset + 1),
121                           (short)storage.length);
122            return (short)(storage.length + 1);
123        } catch (CardRuntimeException e) {
124            return 0x0;
125        }
126    }
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public short backupSize() {
133      return (short)(storage.length + 1);
134    }
135}
136