/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This class represents a token emitted by the GSSContext.wrap()
* call. It is a MessageToken except that it also contains plaintext
* or encrypted data at the end. A wrapToken has certain other rules
* that are peculiar to it and different from a MICToken, which is
* another type of MessageToken. All data in a WrapToken is prepended
* by a random counfounder of 8 bytes. All data in a WrapToken is
* also padded with one to eight bytes where all bytes are equal in
* value to the number of bytes being padded. Thus, all application
* data is replaced by (confounder || data || padding).
*
* @author Mayank Upadhyay
*/
/**
* The size of the random confounder used in a WrapToken.
*/
/*
* The padding used with a WrapToken. All data is padded to the
* next multiple of 8 bytes, even if its length is already
* multiple of 8.
* Use this table as a quick way to obtain padding bytes by
* indexing it with the number of padding bytes required.
*/
static final byte[][] pads = {
null, // No, no one escapes padding
{0x01},
{0x02, 0x02},
{0x03, 0x03, 0x03},
{0x04, 0x04, 0x04, 0x04},
{0x05, 0x05, 0x05, 0x05, 0x05},
{0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},
{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}
};
/*
* A token may come in either in an InputStream or as a
* byte[]. Store a reference to it in either case and process
* it's data only later when getData() is called and
* decryption/copying is needed to be done. Note that JCE can
* decrypt both from a byte[] and from an InputStream.
*/
private boolean readTokenFromInputStream = true;
/*
* Application data may come from an InputStream or from a
* byte[]. However, it will always be stored and processed as a
* byte[] since
* (a) the MessageDigest class only accepts a byte[] as input and
* (b) It allows writing to an OuputStream via a CipherOutputStream.
*/
// the len of the token data: (confounder || data || padding)
// Accessed by CipherHelper
private boolean privacy = false;
/**
* Constructs a WrapToken from token bytes obtained from the
* peer.
* @param context the mechanism context associated with this
* token
* @param tokenBytes the bytes of the token
* @param tokenOffset the offset of the token
* @param tokenLen the length of the token
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective
*/
// Just parse the MessageToken part first
this.readTokenFromInputStream = false;
// Will need the token bytes again when extracting data
this.tokenBytes = tokenBytes;
this.tokenOffset = tokenOffset;
dataSize =
}
/**
* Constructs a WrapToken from token bytes read on the fly from
* an InputStream.
* @param context the mechanism context associated with this
* token
* @param is the InputStream containing the token bytes
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective or if there is
* a problem reading from the InputStream
*/
throws GSSException {
// Just parse the MessageToken part first
// Will need the token bytes again when extracting data
/*
debug("WrapToken Cons: gssHeader.getMechTokenLength=" +
getGSSHeader().getMechTokenLength());
debug("\n token size="
+ getTokenSize());
*/
dataSize =
// debug("\n dataSize=" + dataSize);
// debug("\n");
}
/**
* Obtains the application data that was transmitted in this
* WrapToken.
* @return a byte array containing the application data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
// Remove the confounder and the padding
return retVal;
}
/**
* Obtains the application data that was transmitted in this
* WrapToken, writing it into an application provided output
* array.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @return the size of the data written
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
throws GSSException {
else
}
/**
* Helper routine to obtain the application data transmitted in
* this WrapToken. It is called if the WrapToken was constructed
* with a byte array as input.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
throws GSSException {
int dataPos = tokenOffset +
"Insufficient data in "
+ getTokenName(getTokenId()));
// debug("WrapToken cons: data is token is [" +
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
confounder = new byte[CONFOUNDER_SIZE];
// Do decryption if this token was privacy protected.
if (privacy) {
cipherHelper.decryptData(this,
/*
debug("\t\tDecrypted data is [" +
getHexBytes(confounder) + " " +
getHexBytes(dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE - padding.length) +
getHexBytes(padding) +
"]\n");
*/
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
if (padSize < 0)
padSize = 0;
if (padSize > 8)
padSize %= 8;
// debug("\t\tPadding applied was: " + padSize + "\n");
// byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];
// System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
// debugbuf, 0, debugbuf.length);
// debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));
}
/*
* Make sure sign and sequence number are not corrupt
*/
padding))
"Corrupt checksum or sequence number in Wrap token");
}
/**
* Helper routine to obtain the application data transmitted in
* this WrapToken. It is called if the WrapToken was constructed
* with an Inputstream.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
throws GSSException {
// Don't check the token length. Data will be read on demand from
// the InputStream.
// debug("WrapToken cons: data will be read from InputStream.\n");
confounder = new byte[CONFOUNDER_SIZE];
try {
// Do decryption if this token was privacy protected.
if (privacy) {
// debug("\t\tDecrypted data is [" +
// getHexBytes(confounder) + " " +
// getHexBytes(dataBuf, dataBufOffset,
// dataSize - CONFOUNDER_SIZE - padding.length) +
// getHexBytes(padding) +
// "]\n");
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
if (cipherHelper.isArcFour()) {
} else {
// Data is always a multiple of 8 with this GSS Mech
// Copy all but last block as they are
int offset = dataBufOffset;
for (int i = 0; i < numBlocks; i++) {
offset += 8;
}
byte[] finalBlock = new byte[8];
// debug("\t\tPadding applied was: " + padSize + "\n");
}
}
} catch (IOException e) {
+ ": " + e.getMessage());
}
/*
* Make sure sign and sequence number are not corrupt
*/
padding))
"Corrupt checksum or sequence number in Wrap token");
}
/**
* Helper routine to pick the right padding for a certain length
* of application data. Every application message has some
* padding between 1 and 8 bytes.
* @param len the length of the application data
* @return the padding to be applied
*/
int padSize = 0;
// For RC4-HMAC, all padding is rounded up to 1 byte.
// One byte is needed to say that there is 1 byte of padding.
if (cipherHelper.isArcFour()) {
padSize = 1;
} else {
}
}
throws GSSException {
this.dataOffset = dataOffset;
/*
debug("\nWrapToken cons: data to wrap is [" +
getHexBytes(confounder) + " " +
getHexBytes(dataBytes, dataOffset, dataLen) + " " +
// padding is never null for Wrap
getHexBytes(padding) + "]\n");
*/
padding);
/*
* If the application decides to ask for privacy when the context
* did not negotiate for it, do not provide it. The peer might not
* have support for it. The app will realize this with a call to
* pop.getPrivacy() after wrap().
*/
if (!context.getConfState())
prop.setPrivacy(false);
}
// debug("Writing data: [");
if (!privacy) {
// debug(getHexBytes(confounder, confounder.length));
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
// debug(" " + getHexBytes(padding, padding.length));
} else {
}
// debug("]\n");
}
// XXX Fine tune this initial size
return bos.toByteArray();
}
throws IOException, GSSException {
// Token header is small
// debug("WrapToken.encode: Writing data: [");
if (!privacy) {
// debug(getHexBytes(confounder, confounder.length));
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
dataLen);
// debug(" " + getHexBytes(padding, padding.length));
} else {
// debug(getHexBytes(outToken, offset, dataSize));
}
// debug("]\n");
// %%% assume that plaintext length == ciphertext len
}
return (getTokenSize() + dataSize);
}
if (!conf) {
return SEAL_ALG_NONE;
}
// ignore QOP
return cipherHelper.getSealAlg();
}
// This implementation is way too conservative. And it certainly
// doesn't return the maximum limit.
}
}