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.AID;
20import javacard.framework.CardRuntimeException;
21import javacard.framework.JCSystem;
22import javacard.framework.Shareable;
23import javacard.framework.Util;
24
25import com.nxp.ls.library.LSBackup;
26import com.nxp.ls.library.LSPullModeRestore;
27import com.nxp.ls.library.LSPushModeBackup;
28
29import com.android.verifiedboot.storage.DefaultOsBackupImpl;
30import com.android.verifiedboot.storage.OsBackupInterface;
31
32public class JcopBackupImpl extends DefaultOsBackupImpl implements LSBackup {
33    final public static short MAGIC = (short)0xdeed;
34
35    // From NXP, the AID of the LoaderService.
36    private static final byte[] LS_AID = {
37        // NXP RID
38        (byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x96,
39        // NXP JCOP Applets
40        (byte)0x54, (byte)0x43, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
41        // Family
42        (byte)0x00, (byte)0x0B,
43        // Version
44        (byte)0x00, // LS Application Instance
45        // Type
46        (byte)0x01, // LS Application
47    };
48
49
50    /**
51     * Returns true on a successful reimport of data.
52     *
53     * @param iarray to read from
54     * @param offset to begin copying from.
55     */
56    @Override
57    public boolean restore(byte[] in, short offset) {
58        LSPullModeRestore restore = getLSPullModeRestore();
59        offset = (short) 0;
60        if (restore == null) {
61            return false;
62        }
63        if (restore.start() == (short) 0) {
64            return false;
65        }
66        short length = (short) 5;
67        if (restore.pullData(in, offset, length) == false) {
68            return false;
69        }
70        byte tag = in[0];
71        if (tag != TAG_MAGIC) {
72            return false;
73        }
74        if (Util.getShort(in, (short)1) != (short)2) {
75            return false;
76        }
77        if (Util.getShort(in, (short)3) != MAGIC) {
78            return false;
79        }
80        // Magic is good. Let's process all the tags. They should be
81        // serialized in order and if not, we abort.
82        BackupInterface[] objects = tracked();
83        short i = (short) 0;
84        for ( ; i < objects.length; ++i) {
85            // Get tag and length at the same time.
86            length = 3;
87            if (restore.pullData(in, (short)0, length) == false) {
88                return false;
89            }
90            tag = in[0];
91            length = Util.getShort(in, (short)1);
92            if (restore.pullData(in, (short)0, length) == false) {
93                return false;
94            }
95            if (tag == i && objects[i] != null) {
96                objects[i].restore(in, (short)0, length);
97            } // else we skip it.
98        }
99
100        if (restore.end() == false) {
101            return false;
102        }
103        return true;
104    }
105
106    /**
107     * Retrieve the restore interface from the LoaderService.
108     *
109     * @return LSPullModeRestore interface or null
110     */
111    private static LSPullModeRestore getLSPullModeRestore() {
112        try {
113          return (LSPullModeRestore)JCSystem.getAppletShareableInterfaceObject(
114            JCSystem.lookupAID(LS_AID, (short)(0), (byte)(LS_AID.length)),
115            LSPullModeRestore.LS_PULL_MODE_RESTORE_PARAMETER);
116        } catch (CardRuntimeException e) {
117            return null;
118        }
119    }
120
121    /**
122     * Called via LSBackup for saving off data prior to an update.
123     * Only lockStorage has to be stored, but it means that install() would
124     * need to handle any changes in lock sizes.
125     *
126     * TODO(wad) In each LockInterface tag store with last metadataSize.
127     *
128     * @param backup interface to feed data to
129     * @param buffer working buffer that is shared with LSPushModeBackup
130     * @return true on success or false if there is a failure.
131     */
132    public boolean backup(LSPushModeBackup backup, byte[] buffer) {
133        BackupInterface[] objects = tracked();
134        short length = (short) 5;  // magic(TAG, SIZE, MAGIC)
135        short i;
136        for (i = (short)0; i < (short)objects.length; ++i) {
137            length += 3;  // tag, length
138            if (objects[i] != null) {
139                length += objects[i].backupSize();
140            }
141        }
142        // Interface requires mod 16.
143        if (length % 16 != 0) {
144          length += (16 - (length % 16));
145        }
146        if (backup.start(length) == false) {
147            return false;
148        }
149        // Set magic
150        short offset = (short) 0;
151        buffer[offset++] = TAG_MAGIC;
152        Util.setShort(buffer, offset, (short)2);
153        offset += 2;
154        Util.setShort(buffer, offset, MAGIC);
155        offset += 2;
156
157        for (i = (short)0; i < (short)objects.length; ++i) {
158            buffer[offset++] = (byte) i;  // TAG == index.
159            if (objects[i] != null) {
160                Util.setShort(buffer, offset, objects[i].backupSize());
161            } else {
162                Util.setShort(buffer, offset, (short)0);
163            }
164            offset += 2;
165            if (objects[i] != null) {
166                offset += objects[i].backup(buffer, offset);
167            }
168        }
169        // TODO(wad) Worth checking if offset != length.
170        if (backup.pushData(buffer, (short)0, length) == false) {
171            return false;
172        }
173        return backup.end();
174    }
175
176    /**
177     * {@inheritDoc}
178     *
179     * Checks for the calling LoaderService applet first.
180     */
181    @Override
182    public Shareable getShareableInterfaceObject(AID clientAid, byte arg) {
183        AID lsAid = JCSystem.lookupAID(LS_AID, (short)(0), (byte)(LS_AID.length));
184        if (clientAid.equals(lsAid)) {
185            if (arg == LSBackup.LS_BACKUP_PARAMETER) {
186                return this;
187            }
188        }
189        return null;
190    }
191}
192