Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/krb5/WrapToken.java
41161 views
1
/*
2
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.security.jgss.krb5;
27
28
import org.ietf.jgss.*;
29
import sun.security.jgss.*;
30
import java.io.InputStream;
31
import java.io.OutputStream;
32
import java.io.IOException;
33
import java.io.ByteArrayOutputStream;
34
import sun.security.krb5.Confounder;
35
36
/**
37
* This class represents a token emitted by the GSSContext.wrap()
38
* call. It is a MessageToken except that it also contains plaintext
39
* or encrypted data at the end. A wrapToken has certain other rules
40
* that are peculiar to it and different from a MICToken, which is
41
* another type of MessageToken. All data in a WrapToken is prepended
42
* by a random counfounder of 8 bytes. All data in a WrapToken is
43
* also padded with one to eight bytes where all bytes are equal in
44
* value to the number of bytes being padded. Thus, all application
45
* data is replaced by (confounder || data || padding).
46
*
47
* @author Mayank Upadhyay
48
*/
49
class WrapToken extends MessageToken {
50
/**
51
* The size of the random confounder used in a WrapToken.
52
*/
53
static final int CONFOUNDER_SIZE = 8;
54
55
/*
56
* The padding used with a WrapToken. All data is padded to the
57
* next multiple of 8 bytes, even if its length is already
58
* multiple of 8.
59
* Use this table as a quick way to obtain padding bytes by
60
* indexing it with the number of padding bytes required.
61
*/
62
static final byte[][] pads = {
63
null, // No, no one escapes padding
64
{0x01},
65
{0x02, 0x02},
66
{0x03, 0x03, 0x03},
67
{0x04, 0x04, 0x04, 0x04},
68
{0x05, 0x05, 0x05, 0x05, 0x05},
69
{0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
70
{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},
71
{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}
72
};
73
74
/*
75
* A token may come in either in an InputStream or as a
76
* byte[]. Store a reference to it in either case and process
77
* it's data only later when getData() is called and
78
* decryption/copying is needed to be done. Note that JCE can
79
* decrypt both from a byte[] and from an InputStream.
80
*/
81
private boolean readTokenFromInputStream = true;
82
private InputStream is = null;
83
private byte[] tokenBytes = null;
84
private int tokenOffset = 0;
85
private int tokenLen = 0;
86
87
/*
88
* Application data may come from an InputStream or from a
89
* byte[]. However, it will always be stored and processed as a
90
* byte[] since
91
* (a) the MessageDigest class only accepts a byte[] as input and
92
* (b) It allows writing to an OuputStream via a CipherOutputStream.
93
*/
94
private byte[] dataBytes = null;
95
private int dataOffset = 0;
96
private int dataLen = 0;
97
98
// the len of the token data: (confounder || data || padding)
99
private int dataSize = 0;
100
101
// Accessed by CipherHelper
102
byte[] confounder = null;
103
byte[] padding = null;
104
105
private boolean privacy = false;
106
107
/**
108
* Constructs a WrapToken from token bytes obtained from the
109
* peer.
110
* @param context the mechanism context associated with this
111
* token
112
* @param tokenBytes the bytes of the token
113
* @param tokenOffset the offset of the token
114
* @param tokenLen the length of the token
115
* @param prop the MessageProp into which characteristics of the
116
* parsed token will be stored.
117
* @throws GSSException if the token is defective
118
*/
119
public WrapToken(Krb5Context context,
120
byte[] tokenBytes, int tokenOffset, int tokenLen,
121
MessageProp prop) throws GSSException {
122
123
// Just parse the MessageToken part first
124
super(Krb5Token.WRAP_ID, context,
125
tokenBytes, tokenOffset, tokenLen, prop);
126
127
this.readTokenFromInputStream = false;
128
129
// Will need the token bytes again when extracting data
130
this.tokenBytes = tokenBytes;
131
this.tokenOffset = tokenOffset;
132
this.tokenLen = tokenLen;
133
this.privacy = prop.getPrivacy();
134
dataSize =
135
getGSSHeader().getMechTokenLength() - getKrb5TokenSize();
136
}
137
138
/**
139
* Constructs a WrapToken from token bytes read on the fly from
140
* an InputStream.
141
* @param context the mechanism context associated with this
142
* token
143
* @param is the InputStream containing the token bytes
144
* @param prop the MessageProp into which characteristics of the
145
* parsed token will be stored.
146
* @throws GSSException if the token is defective or if there is
147
* a problem reading from the InputStream
148
*/
149
public WrapToken(Krb5Context context,
150
InputStream is, MessageProp prop)
151
throws GSSException {
152
153
// Just parse the MessageToken part first
154
super(Krb5Token.WRAP_ID, context, is, prop);
155
156
// Will need the token bytes again when extracting data
157
this.is = is;
158
this.privacy = prop.getPrivacy();
159
/*
160
debug("WrapToken Cons: gssHeader.getMechTokenLength=" +
161
getGSSHeader().getMechTokenLength());
162
debug("\n token size="
163
+ getTokenSize());
164
*/
165
166
dataSize =
167
getGSSHeader().getMechTokenLength() - getTokenSize();
168
// debug("\n dataSize=" + dataSize);
169
// debug("\n");
170
}
171
172
/**
173
* Obtains the application data that was transmitted in this
174
* WrapToken.
175
* @return a byte array containing the application data
176
* @throws GSSException if an error occurs while decrypting any
177
* cipher text and checking for validity
178
*/
179
public byte[] getData() throws GSSException {
180
181
byte[] temp = new byte[dataSize];
182
getData(temp, 0);
183
184
// Remove the confounder and the padding
185
byte[] retVal = new byte[dataSize - confounder.length -
186
padding.length];
187
System.arraycopy(temp, 0, retVal, 0, retVal.length);
188
189
return retVal;
190
}
191
192
/**
193
* Obtains the application data that was transmitted in this
194
* WrapToken, writing it into an application provided output
195
* array.
196
* @param dataBuf the output buffer into which the data must be
197
* written
198
* @param dataBufOffset the offset at which to write the data
199
* @return the size of the data written
200
* @throws GSSException if an error occurs while decrypting any
201
* cipher text and checking for validity
202
*/
203
public int getData(byte[] dataBuf, int dataBufOffset)
204
throws GSSException {
205
206
if (readTokenFromInputStream)
207
getDataFromStream(dataBuf, dataBufOffset);
208
else
209
getDataFromBuffer(dataBuf, dataBufOffset);
210
211
return (dataSize - confounder.length - padding.length);
212
}
213
214
/**
215
* Helper routine to obtain the application data transmitted in
216
* this WrapToken. It is called if the WrapToken was constructed
217
* with a byte array as input.
218
* @param dataBuf the output buffer into which the data must be
219
* written
220
* @param dataBufOffset the offset at which to write the data
221
* @throws GSSException if an error occurs while decrypting any
222
* cipher text and checking for validity
223
*/
224
private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset)
225
throws GSSException {
226
227
GSSHeader gssHeader = getGSSHeader();
228
int dataPos = tokenOffset +
229
gssHeader.getLength() + getTokenSize();
230
231
if (dataPos + dataSize > tokenOffset + tokenLen)
232
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
233
"Insufficient data in "
234
+ getTokenName(getTokenId()));
235
236
// debug("WrapToken cons: data is token is [" +
237
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
238
239
confounder = new byte[CONFOUNDER_SIZE];
240
241
// Do decryption if this token was privacy protected.
242
243
if (privacy) {
244
cipherHelper.decryptData(this,
245
tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset);
246
/*
247
debug("\t\tDecrypted data is [" +
248
getHexBytes(confounder) + " " +
249
getHexBytes(dataBuf, dataBufOffset,
250
dataSize - CONFOUNDER_SIZE - padding.length) +
251
getHexBytes(padding) +
252
"]\n");
253
*/
254
255
} else {
256
257
// Token data is in cleartext
258
// debug("\t\tNo encryption was performed by peer.\n");
259
System.arraycopy(tokenBytes, dataPos,
260
confounder, 0, CONFOUNDER_SIZE);
261
int padSize = tokenBytes[dataPos + dataSize - 1];
262
if (padSize < 0)
263
padSize = 0;
264
if (padSize > 8)
265
padSize %= 8;
266
267
padding = pads[padSize];
268
// debug("\t\tPadding applied was: " + padSize + "\n");
269
270
System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
271
dataBuf, dataBufOffset, dataSize -
272
CONFOUNDER_SIZE - padSize);
273
274
// byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];
275
// System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
276
// debugbuf, 0, debugbuf.length);
277
// debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));
278
}
279
280
/*
281
* Make sure sign and sequence number are not corrupt
282
*/
283
284
if (!verifySignAndSeqNumber(confounder,
285
dataBuf, dataBufOffset,
286
dataSize - CONFOUNDER_SIZE
287
- padding.length,
288
padding))
289
throw new GSSException(GSSException.BAD_MIC, -1,
290
"Corrupt checksum or sequence number in Wrap token");
291
}
292
293
/**
294
* Helper routine to obtain the application data transmitted in
295
* this WrapToken. It is called if the WrapToken was constructed
296
* with an Inputstream.
297
* @param dataBuf the output buffer into which the data must be
298
* written
299
* @param dataBufOffset the offset at which to write the data
300
* @throws GSSException if an error occurs while decrypting any
301
* cipher text and checking for validity
302
*/
303
private void getDataFromStream(byte[] dataBuf, int dataBufOffset)
304
throws GSSException {
305
306
GSSHeader gssHeader = getGSSHeader();
307
308
// Don't check the token length. Data will be read on demand from
309
// the InputStream.
310
311
// debug("WrapToken cons: data will be read from InputStream.\n");
312
313
confounder = new byte[CONFOUNDER_SIZE];
314
315
try {
316
317
// Do decryption if this token was privacy protected.
318
319
if (privacy) {
320
cipherHelper.decryptData(this, is, dataSize,
321
dataBuf, dataBufOffset);
322
323
// debug("\t\tDecrypted data is [" +
324
// getHexBytes(confounder) + " " +
325
// getHexBytes(dataBuf, dataBufOffset,
326
// dataSize - CONFOUNDER_SIZE - padding.length) +
327
// getHexBytes(padding) +
328
// "]\n");
329
330
} else {
331
332
// Token data is in cleartext
333
// debug("\t\tNo encryption was performed by peer.\n");
334
readFully(is, confounder);
335
336
if (cipherHelper.isArcFour()) {
337
padding = pads[1];
338
readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1);
339
} else {
340
// Data is always a multiple of 8 with this GSS Mech
341
// Copy all but last block as they are
342
int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1;
343
int offset = dataBufOffset;
344
for (int i = 0; i < numBlocks; i++) {
345
readFully(is, dataBuf, offset, 8);
346
offset += 8;
347
}
348
349
byte[] finalBlock = new byte[8];
350
readFully(is, finalBlock);
351
352
int padSize = finalBlock[7];
353
padding = pads[padSize];
354
355
// debug("\t\tPadding applied was: " + padSize + "\n");
356
System.arraycopy(finalBlock, 0, dataBuf, offset,
357
finalBlock.length - padSize);
358
}
359
}
360
} catch (IOException e) {
361
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
362
getTokenName(getTokenId())
363
+ ": " + e.getMessage());
364
}
365
366
/*
367
* Make sure sign and sequence number are not corrupt
368
*/
369
370
if (!verifySignAndSeqNumber(confounder,
371
dataBuf, dataBufOffset,
372
dataSize - CONFOUNDER_SIZE
373
- padding.length,
374
padding))
375
throw new GSSException(GSSException.BAD_MIC, -1,
376
"Corrupt checksum or sequence number in Wrap token");
377
}
378
379
380
/**
381
* Helper routine to pick the right padding for a certain length
382
* of application data. Every application message has some
383
* padding between 1 and 8 bytes.
384
* @param len the length of the application data
385
* @return the padding to be applied
386
*/
387
private byte[] getPadding(int len) {
388
int padSize = 0;
389
// For RC4-HMAC, all padding is rounded up to 1 byte.
390
// One byte is needed to say that there is 1 byte of padding.
391
if (cipherHelper.isArcFour()) {
392
padSize = 1;
393
} else {
394
padSize = len % 8;
395
padSize = 8 - padSize;
396
}
397
return pads[padSize];
398
}
399
400
public WrapToken(Krb5Context context, MessageProp prop,
401
byte[] dataBytes, int dataOffset, int dataLen)
402
throws GSSException {
403
404
super(Krb5Token.WRAP_ID, context);
405
406
confounder = Confounder.bytes(CONFOUNDER_SIZE);
407
408
padding = getPadding(dataLen);
409
dataSize = confounder.length + dataLen + padding.length;
410
this.dataBytes = dataBytes;
411
this.dataOffset = dataOffset;
412
this.dataLen = dataLen;
413
414
/*
415
debug("\nWrapToken cons: data to wrap is [" +
416
getHexBytes(confounder) + " " +
417
getHexBytes(dataBytes, dataOffset, dataLen) + " " +
418
// padding is never null for Wrap
419
getHexBytes(padding) + "]\n");
420
*/
421
422
genSignAndSeqNumber(prop,
423
confounder,
424
dataBytes, dataOffset, dataLen,
425
padding);
426
427
/*
428
* If the application decides to ask for privacy when the context
429
* did not negotiate for it, do not provide it. The peer might not
430
* have support for it. The app will realize this with a call to
431
* pop.getPrivacy() after wrap().
432
*/
433
if (!context.getConfState())
434
prop.setPrivacy(false);
435
436
privacy = prop.getPrivacy();
437
}
438
439
public void encode(OutputStream os) throws IOException, GSSException {
440
441
super.encode(os);
442
443
// debug("Writing data: [");
444
if (!privacy) {
445
446
// debug(getHexBytes(confounder, confounder.length));
447
os.write(confounder);
448
449
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
450
os.write(dataBytes, dataOffset, dataLen);
451
452
// debug(" " + getHexBytes(padding, padding.length));
453
os.write(padding);
454
455
} else {
456
457
cipherHelper.encryptData(this, confounder,
458
dataBytes, dataOffset, dataLen, padding, os);
459
}
460
// debug("]\n");
461
}
462
463
public byte[] encode() throws IOException, GSSException {
464
// XXX Fine tune this initial size
465
ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50);
466
encode(bos);
467
return bos.toByteArray();
468
}
469
470
public int encode(byte[] outToken, int offset)
471
throws IOException, GSSException {
472
473
// Token header is small
474
ByteArrayOutputStream bos = new ByteArrayOutputStream();
475
super.encode(bos);
476
byte[] header = bos.toByteArray();
477
System.arraycopy(header, 0, outToken, offset, header.length);
478
offset += header.length;
479
480
// debug("WrapToken.encode: Writing data: [");
481
if (!privacy) {
482
483
// debug(getHexBytes(confounder, confounder.length));
484
System.arraycopy(confounder, 0, outToken, offset,
485
confounder.length);
486
offset += confounder.length;
487
488
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
489
System.arraycopy(dataBytes, dataOffset, outToken, offset,
490
dataLen);
491
offset += dataLen;
492
493
// debug(" " + getHexBytes(padding, padding.length));
494
System.arraycopy(padding, 0, outToken, offset, padding.length);
495
496
} else {
497
498
cipherHelper.encryptData(this, confounder, dataBytes,
499
dataOffset, dataLen, padding, outToken, offset);
500
501
// debug(getHexBytes(outToken, offset, dataSize));
502
}
503
504
// debug("]\n");
505
506
// %%% assume that plaintext length == ciphertext len
507
return (header.length + confounder.length + dataLen + padding.length);
508
509
}
510
511
protected int getKrb5TokenSize() throws GSSException {
512
return (getTokenSize() + dataSize);
513
}
514
515
protected int getSealAlg(boolean conf, int qop) throws GSSException {
516
if (!conf) {
517
return SEAL_ALG_NONE;
518
}
519
520
// ignore QOP
521
return cipherHelper.getSealAlg();
522
}
523
524
// This implementation is way too conservative. And it certainly
525
// doesn't return the maximum limit.
526
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
527
CipherHelper ch) throws GSSException {
528
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
529
(getTokenSize(ch) + CONFOUNDER_SIZE) - 8); /* safety */
530
}
531
532
}
533
534