Repository navigation
Expand file tree
/
Copy pathProtectedByteArray.java
More file actions
267 lines (231 loc) · 9.63 KB
/
Copy pathProtectedByteArray.java
File metadata and controls
267 lines (231 loc) · 9.63 KB
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
264
265
266
267
/*
* Copyright (c) 2017, DB Systel GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Frank Schwab, DB Systel GmbH
*
* Changes:
* 2016-09-26: V4.1.0: Created
* 2016-11-24: V4.2.0: Make "isValid" property of underlying array publicly available.
* 2017-12-21: V4.2.1: Added "throws" tags.
*/
package dbscryptolib;
import java.security.SecureRandom;
import java.util.Arrays;
/**
* Stores a byte array in a protected form where "protection" means that 1. the
* data are only stored in an obfuscated form and 2. the data are cleared from
* memory when "close" is called.
*
* Note: The content of the byte array can not be changed after it
* has been set with the constructor.
*
* @author Frank Schwab
* @version 4.2.1
*/
public final class ProtectedByteArray implements AutoCloseable {
private final ShuffledByteArray protectedArray;
private final ShuffledByteArray obfuscation;
/**
* Creates a new <code>ProtectedByteArray</code> for the specified data.
*
* @param arrayToProtect The byte array to protect.
* @throws IllegalArgumentException if <code>arrayToProtect</code> is null.
*/
public ProtectedByteArray(byte[] arrayToProtect) throws IllegalArgumentException {
this.protectedArray = new ShuffledByteArray(arrayToProtect);
this.obfuscation = createNewObfuscationArray(arrayToProtect.length);
storeInObfuscatedArray(arrayToProtect);
}
/**
* Creates a new <code>ProtectedByteArray</code> for the specified data
* starting from <code>offset</code> with length <code>len</code>.
*
* @param arrayToProtect The byte array to protect.
* @param offset The offset of the data in the byte array.
* @param len The length of the data in the byte array.
* @throws ArrayIndexOutOfBoundsException if <code>offset</code> or
* <code>len</code> are less than 0.
* @throws IllegalArgumentException if <code>arrayToProtect</code> is not
* long enough to get <code>len</code> bytes from position
* <code>offset</code> in array <code>arrayToProtect</code>.
*/
public ProtectedByteArray(byte[] arrayToProtect, int offset, int len) throws ArrayIndexOutOfBoundsException, IllegalArgumentException {
checkArray(arrayToProtect);
checkOffsetAndLength(arrayToProtect, offset, len);
final byte[] intermediateArray = new byte[len];
System.arraycopy(arrayToProtect, offset, intermediateArray, 0, len);
this.protectedArray = new ShuffledByteArray(intermediateArray);
this.obfuscation = createNewObfuscationArray(len);
storeInObfuscatedArray(intermediateArray);
Arrays.fill(intermediateArray, (byte) 0); // Clear sensitive data
}
/*
* Check methods
*/
/**
* Checks whether array is valid
* <p>
* Note: An array length of 0 is allowed.
* </p>
*
* @param arrayToProtect Key as byte array
* @throws IllegalArgumentException if <code>arrayToProtect</code> is null
*/
private void checkArray(byte[] arrayToProtect) throws IllegalArgumentException {
if (arrayToProtect == null) {
throw new IllegalArgumentException("arrayToProtect is null");
}
}
/**
* Checks whether offset and length are valid for the array
*
* @param arrayToProtect Key as byte array
* @param offset The offset of the data in the byte array.
* @param len The length of the data in the byte array.
* @throws ArrayIndexOutOfBoundsException if <code>offset</code> or
* <code>len</code> are less than 0.
* @throws IllegalArgumentException if <code>arrayToProtect</code> is not
* long enough to get <code>len</code> bytes from position
* <code>offset</code> in array <code>arrayToProtect</code>.
*/
private void checkOffsetAndLength(byte[] arrayToProtect, int offset, int len) throws IllegalArgumentException {
if ((offset < 0) || (len < 0)) {
throw new ArrayIndexOutOfBoundsException("offset < 0 || len < 0");
}
if ((arrayToProtect.length - offset) < len) {
throw new IllegalArgumentException("arrayToProtect too short for offset and length");
}
}
/*
* Methods for obfuscation and deobfuscation
*/
/**
* Creates a new obfuscation array
*
* @param arrayLength Length of the new obfuscation array
* @return New obfuscation array as ShuffledByteArray
*/
private ShuffledByteArray createNewObfuscationArray(int arrayLength) {
byte [] obfuscationSource = new byte [arrayLength];
final SecureRandom sprng = new SecureRandom();
final ShuffledByteArray result;
sprng.nextBytes(obfuscationSource);
result = new ShuffledByteArray(obfuscationSource);
Arrays.fill(obfuscationSource, (byte) 0); // Clear sensitive data
return result;
}
/**
* Stores the source xored with the
* obfuscation bytes in the protected array.
*/
private void storeInObfuscatedArray(byte[] source) {
// Need to cast a byte xor to a byte as Java does not define an
// xor operation on bytes but silently converts the bytes to
// ints before doing the xor. One more Java stupidity.
for (int i = 0; i < source.length; i++) {
this.protectedArray.setAt(i, (byte) (source[i] ^ this.obfuscation.getAt(i)));
}
}
/**
* Xors the obfuscated array to get the clear data
*
* @return Byte array of clear data
*/
private byte[] getDeObfuscatedArray() {
byte[] result = new byte[this.protectedArray.length()];
// Need to cast a byte xor to a byte as Java does not define an
// xor operation on bytes but silently converts the bytes to
// ints before doing the xor. One more Java stupidity.
for (int i = 0; i < this.protectedArray.length(); i++) {
result[i] = (byte) (this.protectedArray.getAt(i) ^ this.obfuscation.getAt(i));
}
return result;
}
/*
* Access methods
*/
/**
* Returns the data of the byte array in the clear.
*
* @return the data in the byte array.
* @throws IllegalStateException if the protected array has already been
* destroyed.
*/
public byte[] getData() throws IllegalStateException {
// 1. Get the deobfuscated bytes
byte[] result = getDeObfuscatedArray();
return result;
}
/**
* Returns the hash code of this <code>ProtectedByteArray</code> instance.
*
* @return The hash code.
* @throws IllegalStateException if the protected array has already been
* destroyed.
*/
@Override
public int hashCode() throws IllegalStateException {
return this.protectedArray.hashCode();
}
/**
* Compares the specified object with this <code>ProtectedByteArray</code>
* instance.
*
* @param obj The object to compare.
* @return true if byte arrays of both object are equal, otherwise false.
* @throws IllegalStateException if the protected array has already been
* destroyed.
*/
@Override
public boolean equals(Object obj) throws IllegalStateException {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ProtectedByteArray other = (ProtectedByteArray) obj;
final byte[] thisClearKey = this.getData();
final byte[] otherClearKey = other.getData();
boolean result = Arrays.equals(thisClearKey, otherClearKey);
Arrays.fill(thisClearKey, (byte) 0);
Arrays.fill(otherClearKey, (byte) 0);
return result;
}
/**
* Check whether this <code>ProtectedByteArray</code> contains valid data
* @return <code>true</code>: Data are valid. <code>false</code>: Data are not valid.
*/
public boolean isValid() {
return this.protectedArray.isValid();
}
/*
* Method for AutoCloseable interface
*/
/**
* Secure deletion of byte array.
*
* This method is idempotent and never throws an exception.
*/
@Override
public void close() {
this.protectedArray.close();
this.obfuscation.close();
}
}