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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
|
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Slog;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
/**
* Represents encryption parameters used to read a container.
*
* @hide
*/
public class ContainerEncryptionParams implements Parcelable {
protected static final String TAG = "ContainerEncryptionParams";
/** What we print out first when toString() is called. */
private static final String TO_STRING_PREFIX = "ContainerEncryptionParams{";
/**
* Parameter type for parceling that indicates the next parameters are
* IvParameters.
*/
private static final int ENC_PARAMS_IV_PARAMETERS = 1;
/** Parameter type for paceling that indicates there are no MAC parameters. */
private static final int MAC_PARAMS_NONE = 1;
/** The encryption algorithm used. */
private final String mEncryptionAlgorithm;
/** The parameter spec to be used for encryption. */
private final IvParameterSpec mEncryptionSpec;
/** Secret key to be used for decryption. */
private final SecretKey mEncryptionKey;
/** Algorithm name for the MAC to be used. */
private final String mMacAlgorithm;
/** The parameter spec to be used for the MAC tag authentication. */
private final AlgorithmParameterSpec mMacSpec;
/** Secret key to be used for MAC tag authentication. */
private final SecretKey mMacKey;
/** MAC tag authenticating the data in the container. */
private final byte[] mMacTag;
/** Offset into file where authenticated (e.g., MAC protected) data begins. */
private final long mAuthenticatedDataStart;
/** Offset into file where encrypted data begins. */
private final long mEncryptedDataStart;
/**
* Offset into file for the end of encrypted data (and, by extension,
* authenticated data) in file.
*/
private final long mDataEnd;
public ContainerEncryptionParams(String encryptionAlgorithm,
AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey)
throws InvalidAlgorithmParameterException {
this(encryptionAlgorithm, encryptionSpec, encryptionKey, null, null, null, null, -1, -1,
-1);
}
/**
* Creates container encryption specifications for installing from encrypted
* containers.
*
* @param encryptionAlgorithm encryption algorithm to use; format matches
* JCE
* @param encryptionSpec algorithm parameter specification
* @param encryptionKey key used for decryption
* @param macAlgorithm MAC algorithm to use; format matches JCE
* @param macSpec algorithm parameters specification, may be {@code null}
* @param macKey key used for authentication (i.e., for the MAC tag)
* @param macTag message authentication code (MAC) tag for the authenticated
* data
* @param authenticatedDataStart offset of start of authenticated data in
* stream
* @param encryptedDataStart offset of start of encrypted data in stream
* @param dataEnd offset of the end of both the authenticated and encrypted
* data
* @throws InvalidAlgorithmParameterException
*/
public ContainerEncryptionParams(String encryptionAlgorithm,
AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey, String macAlgorithm,
AlgorithmParameterSpec macSpec, SecretKey macKey, byte[] macTag,
long authenticatedDataStart, long encryptedDataStart, long dataEnd)
throws InvalidAlgorithmParameterException {
if (TextUtils.isEmpty(encryptionAlgorithm)) {
throw new NullPointerException("algorithm == null");
} else if (encryptionSpec == null) {
throw new NullPointerException("encryptionSpec == null");
} else if (encryptionKey == null) {
throw new NullPointerException("encryptionKey == null");
}
if (!TextUtils.isEmpty(macAlgorithm)) {
if (macKey == null) {
throw new NullPointerException("macKey == null");
}
}
if (!(encryptionSpec instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Unknown parameter spec class; must be IvParameters");
}
mEncryptionAlgorithm = encryptionAlgorithm;
mEncryptionSpec = (IvParameterSpec) encryptionSpec;
mEncryptionKey = encryptionKey;
mMacAlgorithm = macAlgorithm;
mMacSpec = macSpec;
mMacKey = macKey;
mMacTag = macTag;
mAuthenticatedDataStart = authenticatedDataStart;
mEncryptedDataStart = encryptedDataStart;
mDataEnd = dataEnd;
}
public String getEncryptionAlgorithm() {
return mEncryptionAlgorithm;
}
public AlgorithmParameterSpec getEncryptionSpec() {
return mEncryptionSpec;
}
public SecretKey getEncryptionKey() {
return mEncryptionKey;
}
public String getMacAlgorithm() {
return mMacAlgorithm;
}
public AlgorithmParameterSpec getMacSpec() {
return mMacSpec;
}
public SecretKey getMacKey() {
return mMacKey;
}
public byte[] getMacTag() {
return mMacTag;
}
public long getAuthenticatedDataStart() {
return mAuthenticatedDataStart;
}
public long getEncryptedDataStart() {
return mEncryptedDataStart;
}
public long getDataEnd() {
return mDataEnd;
}
@Override
public int describeContents() {
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ContainerEncryptionParams)) {
return false;
}
final ContainerEncryptionParams other = (ContainerEncryptionParams) o;
// Primitive comparison
if ((mAuthenticatedDataStart != other.mAuthenticatedDataStart)
|| (mEncryptedDataStart != other.mEncryptedDataStart)
|| (mDataEnd != other.mDataEnd)) {
return false;
}
// String comparison
if (!mEncryptionAlgorithm.equals(other.mEncryptionAlgorithm)
|| !mMacAlgorithm.equals(other.mMacAlgorithm)) {
return false;
}
// Object comparison
if (!isSecretKeyEqual(mEncryptionKey, other.mEncryptionKey)
|| !isSecretKeyEqual(mMacKey, other.mMacKey)) {
return false;
}
if (!Arrays.equals(mEncryptionSpec.getIV(), other.mEncryptionSpec.getIV())
|| !Arrays.equals(mMacTag, other.mMacTag) || (mMacSpec != other.mMacSpec)) {
return false;
}
return true;
}
private static final boolean isSecretKeyEqual(SecretKey key1, SecretKey key2) {
final String keyFormat = key1.getFormat();
final String otherKeyFormat = key2.getFormat();
if (keyFormat == null) {
if (keyFormat != otherKeyFormat) {
return false;
}
if (key1.getEncoded() != key2.getEncoded()) {
return false;
}
} else {
if (!keyFormat.equals(key2.getFormat())) {
return false;
}
if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash += 5 * mEncryptionAlgorithm.hashCode();
hash += 7 * Arrays.hashCode(mEncryptionSpec.getIV());
hash += 11 * mEncryptionKey.hashCode();
hash += 13 * mMacAlgorithm.hashCode();
hash += 17 * mMacKey.hashCode();
hash += 19 * Arrays.hashCode(mMacTag);
hash += 23 * mAuthenticatedDataStart;
hash += 29 * mEncryptedDataStart;
hash += 31 * mDataEnd;
return hash;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(TO_STRING_PREFIX);
sb.append("mEncryptionAlgorithm=\"");
sb.append(mEncryptionAlgorithm);
sb.append("\",");
sb.append("mEncryptionSpec=");
sb.append(mEncryptionSpec.toString());
sb.append("mEncryptionKey=");
sb.append(mEncryptionKey.toString());
sb.append("mMacAlgorithm=\"");
sb.append(mMacAlgorithm);
sb.append("\",");
sb.append("mMacSpec=");
sb.append(mMacSpec.toString());
sb.append("mMacKey=");
sb.append(mMacKey.toString());
sb.append(",mAuthenticatedDataStart=");
sb.append(mAuthenticatedDataStart);
sb.append(",mEncryptedDataStart=");
sb.append(mEncryptedDataStart);
sb.append(",mDataEnd=");
sb.append(mDataEnd);
sb.append('}');
return sb.toString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mEncryptionAlgorithm);
dest.writeInt(ENC_PARAMS_IV_PARAMETERS);
dest.writeByteArray(mEncryptionSpec.getIV());
dest.writeSerializable(mEncryptionKey);
dest.writeString(mMacAlgorithm);
dest.writeInt(MAC_PARAMS_NONE);
dest.writeByteArray(new byte[0]);
dest.writeSerializable(mMacKey);
dest.writeByteArray(mMacTag);
dest.writeLong(mAuthenticatedDataStart);
dest.writeLong(mEncryptedDataStart);
dest.writeLong(mDataEnd);
}
private ContainerEncryptionParams(Parcel source) throws InvalidAlgorithmParameterException {
mEncryptionAlgorithm = source.readString();
final int encParamType = source.readInt();
final byte[] encParamsEncoded = source.createByteArray();
mEncryptionKey = (SecretKey) source.readSerializable();
mMacAlgorithm = source.readString();
final int macParamType = source.readInt();
source.createByteArray(); // byte[] macParamsEncoded
mMacKey = (SecretKey) source.readSerializable();
mMacTag = source.createByteArray();
mAuthenticatedDataStart = source.readLong();
mEncryptedDataStart = source.readLong();
mDataEnd = source.readLong();
switch (encParamType) {
case ENC_PARAMS_IV_PARAMETERS:
mEncryptionSpec = new IvParameterSpec(encParamsEncoded);
break;
default:
throw new InvalidAlgorithmParameterException("Unknown parameter type "
+ encParamType);
}
switch (macParamType) {
case MAC_PARAMS_NONE:
mMacSpec = null;
break;
default:
throw new InvalidAlgorithmParameterException("Unknown parameter type "
+ macParamType);
}
if (mEncryptionKey == null) {
throw new NullPointerException("encryptionKey == null");
}
}
public static final Parcelable.Creator<ContainerEncryptionParams> CREATOR =
new Parcelable.Creator<ContainerEncryptionParams>() {
public ContainerEncryptionParams createFromParcel(Parcel source) {
try {
return new ContainerEncryptionParams(source);
} catch (InvalidAlgorithmParameterException e) {
Slog.e(TAG, "Invalid algorithm parameters specified", e);
return null;
}
}
public ContainerEncryptionParams[] newArray(int size) {
return new ContainerEncryptionParams[size];
}
};
}
|