- Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathObjectLocker.java
263 lines (246 loc) · 10.5 KB
/
ObjectLocker.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
packageopencrypto.jcmathlib;
importjavacard.framework.ISOException;
importjavacard.framework.JCSystem;
importjavacard.framework.Util;
/**
*
* @author Vasilios Mavroudis and Petr Svenda
*/
publicclassObjectLocker {
/**
* Configuration flag controlling clearing of shared objects on lock as
* prevention of unwanted leak of sensitive information from previous
* operation. If true, object is erased once locked for use
*/
privatebooleanERASE_ON_LOCK = false;
/**
* Configuration flag controlling clearing of shared objects on lock as
* prevention of unwanted leak of sensitive information to next
* operation. If true, object is erased once unlocked from use
*/
privatebooleanERASE_ON_UNLOCK = false;
/**
* Configuration flag controlling clearing of shared objects on lock as
* prevention of unwanted leak of sensitive information to next operation.
* If true, object is erased once unlocked from use
*/
privatebooleanPROFILE_LOCKED_OBJECTS = false;
/**
* Array of pointers to objects which will be guarded by locks.
* Every even value contains pointer to registered object. Subsequent index
* contains null if not locked, !null if locked,
* Stored in RAM for fast access.
*/
privateObject[] lockedObjects;
/**
* Copy of pointers to objects from lockedObjects in persistent memory to refresh after card reset.
* Refreshed by call {@code refreshAfterReset()}
*/
privateObject[] lockedObjectsPersistent;
/**
* Array to hold state of lock for all other objects implemented as N x N array [0...N-1][N...2N-1]...[]
* where [0...N-1] contains the states of lock for all other objects than first object (lockedObjects[0]).
* If no other object is locked after series of operations, [0...N-1] will contain 0 on all indexes.
* All objects (lockedObjects[i]) which happened to be locked together with have 1 at [0...i...N-1].
*/
publicbyte[] profileLockedObjects;
/**
* If true, locking is performed, otherwise relevant method just return without any operation performed
*/
privatebooleanbLockingActive = true;
publicObjectLocker(shortnumArrays) {
initialize(numArrays, ERASE_ON_LOCK, ERASE_ON_UNLOCK);
}
publicObjectLocker(shortnumArrays, booleanbEraseOnLock, booleanbEraseOnUnlock) {
initialize(numArrays, bEraseOnLock, bEraseOnUnlock);
}
privatefinalvoidinitialize(shortnumObjects, booleanbEraseOnLock, booleanbEraseOnUnlock) {
lockedObjects = JCSystem.makeTransientObjectArray((short) (2 * numObjects), JCSystem.CLEAR_ON_RESET);
lockedObjectsPersistent = newObject[(short) (2 * numObjects)];
ERASE_ON_LOCK = bEraseOnLock;
ERASE_ON_UNLOCK = bEraseOnUnlock;
profileLockedObjects = newbyte[(short) (numObjects * numObjects)];
resetProfileLocks();
}
/**
* Reset profile array with profile locks statistics.
*/
publicvoidresetProfileLocks() {
Util.arrayFillNonAtomic(profileLockedObjects, (short) 0, (short) profileLockedObjects.length, (byte) 0);
}
/**
* Register new object for lock guarding.
* @param objToLock object to be guarded
* @return index to internal array where registered object is stored (if known, lock/unlock is faster)
*/
publicshortregisterLock(ObjectobjToLock) {
shorti;
for (i = 0; i < (short) lockedObjects.length; i += 2) {
if (lockedObjects[i] == null) {
// Free slot found
lockedObjects[i] = objToLock;
lockedObjects[(short) (i + 1)] = null; // null means array is unlocked
lockedObjectsPersistent[i] = objToLock; // Store same into persistent array as well
lockedObjectsPersistent[(short) (i + 1)] = null;
returni; // Return index for potential speedup of locking
}
}
ISOException.throwIt(ReturnCodes.SW_LOCK_NOFREESLOT);
return -1;
}
/**
* Locking array (placed in RAM) must be refreshed after card reset. Call this method during select()
*/
publicvoidrefreshAfterReset() {
for (shorti = 0; i < (short) lockedObjects.length; i++) {
lockedObjects[i] = lockedObjectsPersistent[i];
}
}
/**
* Controls if locking and unlocking is actually performed. The lock operations
* add some overhead, so it may be turned on/off as required. E.g., when developing
* new code or like to enjoy protection of automatic clearing of shared objects before/after lock
* enable this feature.
* @param bLockActive if true, locking and unlocking is performed. If false, lock/unlock methods will return without any effect
*/
publicvoidsetLockingActive(booleanbLockActive) {
bLockingActive = bLockActive;
}
/**
* Lock/reserve provided object for subsequent use. Used to protect corruption
* of pre-allocated shared objects in different, potentially nested,
* operations. Must be unlocked later on.
*
* @param objToLock array to be locked
* @throws SW_ALREADYLOCKED if already locked (is already in use by
* other operation)
*/
publicvoidlock(ObjectobjToLock) {
if (!bLockingActive) {
return;
}
// Find object to lock
shorti;
for (i = 0; i < (short) lockedObjects.length; i += 2) {
if (lockedObjects[i] != null && lockedObjects[i].equals(objToLock)) {
lock(objToLock, i);
break;
}
}
// If reached here, required array was not found
if (i == (short) lockedObjects.length) {
ISOException.throwIt(ReturnCodes.SW_LOCK_OBJECT_NOT_FOUND);
}
}
publicvoidlock(byte[] objToLock) {
if (!bLockingActive) {
return;
}
lock((Object) objToLock);
if (ERASE_ON_LOCK) {
Util.arrayFillNonAtomic(objToLock, (short) 0, (short) objToLock.length, (byte) 0);
}
}
/**
* Unlock/release object from use. Used to protect corruption of
* pre-allocated objects used in different nested operations. Must
* be locked before.
*
* @param objToUnlock object to unlock
* @throws SW_NOTLOCKED_BIGNAT if was not locked before (inconsistence in
* lock/unlock sequence)
*/
publicvoidunlock(ObjectobjToUnlock) {
if (!bLockingActive) {
return;
}
// Find object to unlock
shorti;
for (i = 0; i < (short) lockedObjects.length; i += 2) {
if (lockedObjects[i] != null && lockedObjects[i].equals(objToUnlock)) {
unlock(objToUnlock, i);
break;
}
}
// If reached here, required array was not found
if (i == (short) lockedObjects.length) {
ISOException.throwIt(ReturnCodes.SW_LOCK_OBJECT_NOT_FOUND);
}
}
publicvoidunlock(byte[] objToUnlock) {
if (!bLockingActive) {
return;
}
unlock((Object) objToUnlock);
if (ERASE_ON_UNLOCK) {
Util.arrayFillNonAtomic(objToUnlock, (short) 0, (short) objToUnlock.length, (byte) 0);
}
}
/**
* Unlocks all locked objects
*/
publicvoidunlockAll() {
if (!bLockingActive) {
return;
}
for (shorti = 0; i < (short) lockedObjects.length; i += 2) {
lockedObjects[(short) (i + 1)] = null;
}
}
/**
* Check if provided object is logically locked
* @param objToUnlock object to be checked
* @return true of array is logically locked, false otherwise
*/
publicbooleanisLocked(ObjectobjToUnlock) {
if (!bLockingActive) {
returnfalse;
}
// Find object to unlock
shorti;
for (i = 0; i < (short) lockedObjects.length; i += 2) {
if (lockedObjects[i] != null && lockedObjects[i].equals(objToUnlock)) {
returnlockedObjects[(short) (i + 1)] != null;
}
}
// If reached here, required object was not found
if (i == (short) lockedObjects.length) {
ISOException.throwIt(ReturnCodes.SW_LOCK_OBJECT_NOT_FOUND);
}
returnfalse;
}
privatevoidlock(ObjectobjToLock, shortlockIndex) {
if (lockedObjects[lockIndex] != null && !lockedObjects[lockIndex].equals(objToLock)) {
ISOException.throwIt(ReturnCodes.SW_LOCK_OBJECT_MISMATCH);
}
// Next position in array signalizes logical lock (null == unlocked, !null == locked)
if (lockedObjects[(short) (lockIndex + 1)] == null) {
lockedObjects[(short) (lockIndex + 1)] = objToLock; // lock logically by assigning object reference to [i + 1]
} else {
// this array is already locked, raise exception (incorrect sequence of locking and unlocking)
ISOException.throwIt(ReturnCodes.SW_LOCK_ALREADYLOCKED);
}
if (PROFILE_LOCKED_OBJECTS) {
// If enabled, check status of all other objects and mark these that are currently locked
shortprofileLockOffset = (short) ((short) (lockIndex / 2) * (short) ((short) lockedObjects.length / 2)); // Obtain section of profileLockedObjects array relevant for current object
for (shorti = 0; i < (short) lockedObjects.length; i += 2) {
if (lockedObjects[(short) (i + 1)] != null) {
// Object at index i is locked, mark it to corresponding position in profileLockedObjects by setting value to 1
profileLockedObjects[(short) (profileLockOffset + (short) (i / 2))] = 1;
}
}
}
}
privatevoidunlock(ObjectobjToUnlock, shortlockIndex) {
if (lockedObjects[lockIndex] != null && !lockedObjects[lockIndex].equals(objToUnlock)) {
ISOException.throwIt(ReturnCodes.SW_LOCK_OBJECT_MISMATCH);
}
// Next position in array signalizes logical lock (null == unlocked, !null == locked)
if (lockedObjects[(short) (lockIndex + 1)].equals(objToUnlock)) {
lockedObjects[(short) (lockIndex + 1)] = null; // lock logically by assigning object reference to [i + 1]
} else {
// this array is not locked, raise exception (incorrect sequence of locking and unlocking)
ISOException.throwIt(ReturnCodes.SW_LOCK_NOTLOCKED);
}
}
}