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.JCSystem;
21import javacard.framework.Util;
22
23import javacard.security.KeyBuilder;
24import javacard.security.MessageDigest;
25import javacard.security.RSAPublicKey;
26import javacard.security.Signature;
27
28import com.android.verifiedboot.storage.LockInterface;
29import com.android.verifiedboot.globalstate.owner.OwnerInterface;
30
31class BasicLock implements LockInterface {
32    // Layout: LockValue (byte)
33    private byte[] storage;
34    private short storageOffset;
35    private OwnerInterface globalState;
36    private boolean onlyInBootloader;
37    private boolean onlyInHLOS;
38    private boolean needMetadata;
39    private short metadataSize;
40    private LockInterface[] requiredLocks;
41
42    /**
43     * Initializes the instance.
44     *
45     * @param maxMetadataSize length of the metadata
46     * @param requiredLocks specify the number of locks that unlocking
47     *                      will depend on.
48     *
49     */
50    public BasicLock(short maxMetadataSize, short requiredLockNum) {
51        onlyInBootloader = false;
52        onlyInHLOS = false;
53        needMetadata = false;
54        metadataSize = maxMetadataSize;
55        requiredLocks = new LockInterface[requiredLockNum];
56    }
57
58    /**
59     * {@inheritDoc}
60     */
61    @Override
62    public short backupSize() {
63        return getStorageNeeded();
64    }
65
66
67    /**
68     * {@inheritDoc}
69     */
70    @Override
71    public short backup(byte[] outBytes, short outBytesOffset) {
72        Util.arrayCopy(storage, storageOffset,
73                       outBytes, outBytesOffset,
74                       backupSize());
75        return backupSize();
76    }
77
78    /**
79     * {@inheritDoc}
80     */
81    @Override
82    public boolean restore(byte[] inBytes, short inBytesOffset,
83                           short inBytesLength) {
84        if (inBytesLength > backupSize() || inBytesLength == (short)0) {
85            return false;
86        }
87        Util.arrayCopy(inBytes, inBytesOffset,
88                       storage, storageOffset,
89                       inBytesLength);
90        return true;
91    }
92
93    /**
94     * Indicates that it is required that the {@link #globalState} is
95     * in the bootloader when the lock is changed.
96     *
97     * @param inBootloader  true if changes can only happen in the bootloader.
98     */
99    public void requireBootloader(boolean inBootloader) {
100        onlyInBootloader = inBootloader;
101    }
102
103    /**
104     * Indicates that it is required that the {@link #globalState} is
105     * NOT in the bootloader when the lock is changed.
106     *
107     * @param inHLOS  true if changes can only happen in the bootloader.
108     */
109    public void requireHLOS(boolean inHLOS) {
110        onlyInHLOS = inHLOS;
111    }
112
113
114    /**
115     * Indicates that metadata must be supplied when locking.
116     *
117     * @param atLock  true if metadata must be supplied.
118     */
119    public void requireMetadata(boolean atLock) {
120        needMetadata = atLock;
121    }
122
123    /**
124     * Adds a lock that must be unlocked to enable
125     * this lock to toggle.
126     *
127     * @param lock lock to depend on
128     * @return true on success or false on no space.
129     */
130    public boolean addRequiredLock(LockInterface lock) {
131        for (short i = 0; i < (short) requiredLocks.length; ++i) {
132            if (requiredLocks[i] == null) {
133                requiredLocks[i] = lock;
134                return true;
135            }
136        }
137        return false;
138    }
139
140    /**
141     * {@inheritDoc}
142     *
143     * Return the error states useful for diagnostics.
144     */
145    @Override
146    public short initialized() {
147        if (storage == null) {
148            return 1;
149        }
150        if (globalState == null) {
151            return 2;
152        }
153        return 0;
154    }
155    /**
156     * {@inheritDoc}
157     */
158    @Override
159    public short getStorageNeeded() {
160        return (short)(1 + metadataLength());
161    }
162
163    /**
164     * Sets the backing store to use for state.
165     *
166     * @param extStorage  external array to use for storage
167     * @param extStorageOffset where to begin storing data
168     *
169     * This should be called before use.
170     */
171    @Override
172    public void initialize(OwnerInterface globalStateOwner, byte[] extStorage,
173                           short extStorageOffset) {
174        globalState = globalStateOwner;
175        // Zero it first (in case we are interrupted).
176        Util.arrayFillNonAtomic(extStorage, extStorageOffset,
177                                getStorageNeeded(), (byte) 0x00);
178        storage = extStorage;
179        storageOffset = extStorageOffset;
180    }
181
182    /**
183     * {@inheritDoc}
184     */
185    @Override
186    public short get(byte[] lockOut, short lockOffset) {
187        if (storage == null) {
188            return 0x0001;
189        }
190        try {
191            Util.arrayCopy(storage, storageOffset,
192                           lockOut, lockOffset, (short) 1);
193        } catch (CardRuntimeException e) {
194            return 0x0002;
195        }
196        return 0;
197    }
198
199    /**
200     * {@inheritDoc}
201     *
202     * Returns 0xffff if {@link #initialize()} has not yet been called.
203     */
204    @Override
205    public short lockOffset() {
206        if (storage == null) {
207            return (short) 0xffff;
208        }
209        return storageOffset;
210    }
211
212
213    /**
214     * {@inheritDoc}
215     *
216     */
217    @Override
218    public short metadataOffset() {
219        if (storage == null) {
220            return (short) 0xffff;
221        }
222        return (short)(lockOffset() + 1);
223    }
224
225    /**
226     * {@inheritDoc}
227     *
228     * @return length of metadata.
229     */
230    public short metadataLength() {
231        return metadataSize;
232    }
233
234    /**
235     * Ensures any requiredLocks are unlocked.
236     * @return true if allowed or false if not.
237     */
238    public boolean prerequisitesMet() {
239        if (requiredLocks.length != 0) {
240            byte[] temp = new byte[1];
241            short resp = 0;
242            for (short l = 0; l < requiredLocks.length; ++l) {
243                resp = requiredLocks[l].get(temp, (short) 0);
244                // On error or not cleared, fail.
245                if (resp != 0 || temp[0] != (byte) 0x0) {
246                    return false;
247                }
248            }
249        }
250        return true;
251    }
252
253    /**
254     * {@inheritDoc}
255     *
256     * Returns 0x0 on success.
257     */
258    @Override
259    public short set(byte val) {
260        if (storage == null) {
261            return 0x0001;
262        }
263        // Do not require meta on unlock.
264        if (val != 0) {
265            // While an invalid combo, we can just make the require flag
266            // pointless if metadataLength == 0.
267            if (needMetadata == true && metadataLength() > 0) {
268                return 0x0002;
269            }
270        }
271        // To relock, the lock must be unlocked, then relocked.
272        if (val != (byte)0 && storage[lockOffset()] != (byte)0) {
273            return 0x0005;
274        }
275        if (globalState.production() == true) {
276             // Enforce only when in production.
277            if (onlyInBootloader == true) {
278                // If onlyInBootloader is false, we allow toggling regardless.
279                if (globalState.inBootloader() == false) {
280                    return 0x0003;
281                }
282            }
283            if (onlyInHLOS == true) {
284                 // If onlyInHLOS is false, we allow toggling regardless.
285                if (globalState.inBootloader() == true) {
286                    return 0x0003;
287                }
288            }
289        }
290        if (prerequisitesMet() == false) {
291          return 0x0a00;
292        }
293        try {
294            storage[storageOffset] = val;
295        } catch (CardRuntimeException e) {
296            return 0x0004;
297        }
298        return 0;
299    }
300
301   /**
302     * {@inheritDoc}
303     *
304     * If configured with {@link #requiredMetadata}, will populate the
305     * metadata. Otherwise, it will just call {@link #set}.
306     *
307     */
308    @Override
309    public short setWithMetadata(byte lockValue, byte[] lockMeta,
310                                 short lockMetaOffset, short lockMetaLength) {
311        if (storage == null) {
312            return 0x0001;
313        }
314        // No overruns, please.
315        if (lockMetaLength > metadataLength()) {
316            return 0x0002;
317        }
318        // To relock, the lock must be unlocked, then relocked.
319        // This ensures that a lock like LOCK_OWNER cannot have its key value
320        // changed without first having the permission to unlock and lock again.
321        if (lockValue != (byte)0 && storage[lockOffset()] != (byte)0) {
322            return 0x0005;
323        }
324        if (metadataLength() == 0) {
325            return set(lockValue);
326        }
327        // Before copying, ensure changing the lock state is currently permitted.
328        if (prerequisitesMet() == false) {
329          return 0x0a00;
330        }
331        try {
332            // When unlocking, do so before clearing the metadata.
333            if (lockValue == (byte) 0) {
334                JCSystem.beginTransaction();
335                storage[lockOffset()] = lockValue;
336                JCSystem.commitTransaction();
337            }
338            if (lockMetaLength == 0) {
339                // An empty lockMeta will clear the value.
340                Util.arrayFillNonAtomic(storage, metadataOffset(),
341                                        metadataLength(), (byte) 0x00);
342            } else {
343                Util.arrayCopyNonAtomic(lockMeta, lockMetaOffset,
344                                        storage, metadataOffset(),
345                                        lockMetaLength);
346            }
347            // When locking, do so after the copy as interrupting it will
348            // not impact its use in a locked state.
349            if (lockValue != (byte) 0) {
350                JCSystem.beginTransaction();
351                storage[lockOffset()] = lockValue;
352                JCSystem.commitTransaction();
353            }
354        } catch (CardRuntimeException e) {
355            return 0x0004;
356        }
357        return 0;
358    }
359}
360