Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/krb5/WrapToken.java
41161 views
/*1* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss.krb5;2627import org.ietf.jgss.*;28import sun.security.jgss.*;29import java.io.InputStream;30import java.io.OutputStream;31import java.io.IOException;32import java.io.ByteArrayOutputStream;33import sun.security.krb5.Confounder;3435/**36* This class represents a token emitted by the GSSContext.wrap()37* call. It is a MessageToken except that it also contains plaintext38* or encrypted data at the end. A wrapToken has certain other rules39* that are peculiar to it and different from a MICToken, which is40* another type of MessageToken. All data in a WrapToken is prepended41* by a random counfounder of 8 bytes. All data in a WrapToken is42* also padded with one to eight bytes where all bytes are equal in43* value to the number of bytes being padded. Thus, all application44* data is replaced by (confounder || data || padding).45*46* @author Mayank Upadhyay47*/48class WrapToken extends MessageToken {49/**50* The size of the random confounder used in a WrapToken.51*/52static final int CONFOUNDER_SIZE = 8;5354/*55* The padding used with a WrapToken. All data is padded to the56* next multiple of 8 bytes, even if its length is already57* multiple of 8.58* Use this table as a quick way to obtain padding bytes by59* indexing it with the number of padding bytes required.60*/61static final byte[][] pads = {62null, // No, no one escapes padding63{0x01},64{0x02, 0x02},65{0x03, 0x03, 0x03},66{0x04, 0x04, 0x04, 0x04},67{0x05, 0x05, 0x05, 0x05, 0x05},68{0x06, 0x06, 0x06, 0x06, 0x06, 0x06},69{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},70{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}71};7273/*74* A token may come in either in an InputStream or as a75* byte[]. Store a reference to it in either case and process76* it's data only later when getData() is called and77* decryption/copying is needed to be done. Note that JCE can78* decrypt both from a byte[] and from an InputStream.79*/80private boolean readTokenFromInputStream = true;81private InputStream is = null;82private byte[] tokenBytes = null;83private int tokenOffset = 0;84private int tokenLen = 0;8586/*87* Application data may come from an InputStream or from a88* byte[]. However, it will always be stored and processed as a89* byte[] since90* (a) the MessageDigest class only accepts a byte[] as input and91* (b) It allows writing to an OuputStream via a CipherOutputStream.92*/93private byte[] dataBytes = null;94private int dataOffset = 0;95private int dataLen = 0;9697// the len of the token data: (confounder || data || padding)98private int dataSize = 0;99100// Accessed by CipherHelper101byte[] confounder = null;102byte[] padding = null;103104private boolean privacy = false;105106/**107* Constructs a WrapToken from token bytes obtained from the108* peer.109* @param context the mechanism context associated with this110* token111* @param tokenBytes the bytes of the token112* @param tokenOffset the offset of the token113* @param tokenLen the length of the token114* @param prop the MessageProp into which characteristics of the115* parsed token will be stored.116* @throws GSSException if the token is defective117*/118public WrapToken(Krb5Context context,119byte[] tokenBytes, int tokenOffset, int tokenLen,120MessageProp prop) throws GSSException {121122// Just parse the MessageToken part first123super(Krb5Token.WRAP_ID, context,124tokenBytes, tokenOffset, tokenLen, prop);125126this.readTokenFromInputStream = false;127128// Will need the token bytes again when extracting data129this.tokenBytes = tokenBytes;130this.tokenOffset = tokenOffset;131this.tokenLen = tokenLen;132this.privacy = prop.getPrivacy();133dataSize =134getGSSHeader().getMechTokenLength() - getKrb5TokenSize();135}136137/**138* Constructs a WrapToken from token bytes read on the fly from139* an InputStream.140* @param context the mechanism context associated with this141* token142* @param is the InputStream containing the token bytes143* @param prop the MessageProp into which characteristics of the144* parsed token will be stored.145* @throws GSSException if the token is defective or if there is146* a problem reading from the InputStream147*/148public WrapToken(Krb5Context context,149InputStream is, MessageProp prop)150throws GSSException {151152// Just parse the MessageToken part first153super(Krb5Token.WRAP_ID, context, is, prop);154155// Will need the token bytes again when extracting data156this.is = is;157this.privacy = prop.getPrivacy();158/*159debug("WrapToken Cons: gssHeader.getMechTokenLength=" +160getGSSHeader().getMechTokenLength());161debug("\n token size="162+ getTokenSize());163*/164165dataSize =166getGSSHeader().getMechTokenLength() - getTokenSize();167// debug("\n dataSize=" + dataSize);168// debug("\n");169}170171/**172* Obtains the application data that was transmitted in this173* WrapToken.174* @return a byte array containing the application data175* @throws GSSException if an error occurs while decrypting any176* cipher text and checking for validity177*/178public byte[] getData() throws GSSException {179180byte[] temp = new byte[dataSize];181getData(temp, 0);182183// Remove the confounder and the padding184byte[] retVal = new byte[dataSize - confounder.length -185padding.length];186System.arraycopy(temp, 0, retVal, 0, retVal.length);187188return retVal;189}190191/**192* Obtains the application data that was transmitted in this193* WrapToken, writing it into an application provided output194* array.195* @param dataBuf the output buffer into which the data must be196* written197* @param dataBufOffset the offset at which to write the data198* @return the size of the data written199* @throws GSSException if an error occurs while decrypting any200* cipher text and checking for validity201*/202public int getData(byte[] dataBuf, int dataBufOffset)203throws GSSException {204205if (readTokenFromInputStream)206getDataFromStream(dataBuf, dataBufOffset);207else208getDataFromBuffer(dataBuf, dataBufOffset);209210return (dataSize - confounder.length - padding.length);211}212213/**214* Helper routine to obtain the application data transmitted in215* this WrapToken. It is called if the WrapToken was constructed216* with a byte array as input.217* @param dataBuf the output buffer into which the data must be218* written219* @param dataBufOffset the offset at which to write the data220* @throws GSSException if an error occurs while decrypting any221* cipher text and checking for validity222*/223private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset)224throws GSSException {225226GSSHeader gssHeader = getGSSHeader();227int dataPos = tokenOffset +228gssHeader.getLength() + getTokenSize();229230if (dataPos + dataSize > tokenOffset + tokenLen)231throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,232"Insufficient data in "233+ getTokenName(getTokenId()));234235// debug("WrapToken cons: data is token is [" +236// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");237238confounder = new byte[CONFOUNDER_SIZE];239240// Do decryption if this token was privacy protected.241242if (privacy) {243cipherHelper.decryptData(this,244tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset);245/*246debug("\t\tDecrypted data is [" +247getHexBytes(confounder) + " " +248getHexBytes(dataBuf, dataBufOffset,249dataSize - CONFOUNDER_SIZE - padding.length) +250getHexBytes(padding) +251"]\n");252*/253254} else {255256// Token data is in cleartext257// debug("\t\tNo encryption was performed by peer.\n");258System.arraycopy(tokenBytes, dataPos,259confounder, 0, CONFOUNDER_SIZE);260int padSize = tokenBytes[dataPos + dataSize - 1];261if (padSize < 0)262padSize = 0;263if (padSize > 8)264padSize %= 8;265266padding = pads[padSize];267// debug("\t\tPadding applied was: " + padSize + "\n");268269System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,270dataBuf, dataBufOffset, dataSize -271CONFOUNDER_SIZE - padSize);272273// byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];274// System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,275// debugbuf, 0, debugbuf.length);276// debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));277}278279/*280* Make sure sign and sequence number are not corrupt281*/282283if (!verifySignAndSeqNumber(confounder,284dataBuf, dataBufOffset,285dataSize - CONFOUNDER_SIZE286- padding.length,287padding))288throw new GSSException(GSSException.BAD_MIC, -1,289"Corrupt checksum or sequence number in Wrap token");290}291292/**293* Helper routine to obtain the application data transmitted in294* this WrapToken. It is called if the WrapToken was constructed295* with an Inputstream.296* @param dataBuf the output buffer into which the data must be297* written298* @param dataBufOffset the offset at which to write the data299* @throws GSSException if an error occurs while decrypting any300* cipher text and checking for validity301*/302private void getDataFromStream(byte[] dataBuf, int dataBufOffset)303throws GSSException {304305GSSHeader gssHeader = getGSSHeader();306307// Don't check the token length. Data will be read on demand from308// the InputStream.309310// debug("WrapToken cons: data will be read from InputStream.\n");311312confounder = new byte[CONFOUNDER_SIZE];313314try {315316// Do decryption if this token was privacy protected.317318if (privacy) {319cipherHelper.decryptData(this, is, dataSize,320dataBuf, dataBufOffset);321322// debug("\t\tDecrypted data is [" +323// getHexBytes(confounder) + " " +324// getHexBytes(dataBuf, dataBufOffset,325// dataSize - CONFOUNDER_SIZE - padding.length) +326// getHexBytes(padding) +327// "]\n");328329} else {330331// Token data is in cleartext332// debug("\t\tNo encryption was performed by peer.\n");333readFully(is, confounder);334335if (cipherHelper.isArcFour()) {336padding = pads[1];337readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1);338} else {339// Data is always a multiple of 8 with this GSS Mech340// Copy all but last block as they are341int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1;342int offset = dataBufOffset;343for (int i = 0; i < numBlocks; i++) {344readFully(is, dataBuf, offset, 8);345offset += 8;346}347348byte[] finalBlock = new byte[8];349readFully(is, finalBlock);350351int padSize = finalBlock[7];352padding = pads[padSize];353354// debug("\t\tPadding applied was: " + padSize + "\n");355System.arraycopy(finalBlock, 0, dataBuf, offset,356finalBlock.length - padSize);357}358}359} catch (IOException e) {360throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,361getTokenName(getTokenId())362+ ": " + e.getMessage());363}364365/*366* Make sure sign and sequence number are not corrupt367*/368369if (!verifySignAndSeqNumber(confounder,370dataBuf, dataBufOffset,371dataSize - CONFOUNDER_SIZE372- padding.length,373padding))374throw new GSSException(GSSException.BAD_MIC, -1,375"Corrupt checksum or sequence number in Wrap token");376}377378379/**380* Helper routine to pick the right padding for a certain length381* of application data. Every application message has some382* padding between 1 and 8 bytes.383* @param len the length of the application data384* @return the padding to be applied385*/386private byte[] getPadding(int len) {387int padSize = 0;388// For RC4-HMAC, all padding is rounded up to 1 byte.389// One byte is needed to say that there is 1 byte of padding.390if (cipherHelper.isArcFour()) {391padSize = 1;392} else {393padSize = len % 8;394padSize = 8 - padSize;395}396return pads[padSize];397}398399public WrapToken(Krb5Context context, MessageProp prop,400byte[] dataBytes, int dataOffset, int dataLen)401throws GSSException {402403super(Krb5Token.WRAP_ID, context);404405confounder = Confounder.bytes(CONFOUNDER_SIZE);406407padding = getPadding(dataLen);408dataSize = confounder.length + dataLen + padding.length;409this.dataBytes = dataBytes;410this.dataOffset = dataOffset;411this.dataLen = dataLen;412413/*414debug("\nWrapToken cons: data to wrap is [" +415getHexBytes(confounder) + " " +416getHexBytes(dataBytes, dataOffset, dataLen) + " " +417// padding is never null for Wrap418getHexBytes(padding) + "]\n");419*/420421genSignAndSeqNumber(prop,422confounder,423dataBytes, dataOffset, dataLen,424padding);425426/*427* If the application decides to ask for privacy when the context428* did not negotiate for it, do not provide it. The peer might not429* have support for it. The app will realize this with a call to430* pop.getPrivacy() after wrap().431*/432if (!context.getConfState())433prop.setPrivacy(false);434435privacy = prop.getPrivacy();436}437438public void encode(OutputStream os) throws IOException, GSSException {439440super.encode(os);441442// debug("Writing data: [");443if (!privacy) {444445// debug(getHexBytes(confounder, confounder.length));446os.write(confounder);447448// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));449os.write(dataBytes, dataOffset, dataLen);450451// debug(" " + getHexBytes(padding, padding.length));452os.write(padding);453454} else {455456cipherHelper.encryptData(this, confounder,457dataBytes, dataOffset, dataLen, padding, os);458}459// debug("]\n");460}461462public byte[] encode() throws IOException, GSSException {463// XXX Fine tune this initial size464ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50);465encode(bos);466return bos.toByteArray();467}468469public int encode(byte[] outToken, int offset)470throws IOException, GSSException {471472// Token header is small473ByteArrayOutputStream bos = new ByteArrayOutputStream();474super.encode(bos);475byte[] header = bos.toByteArray();476System.arraycopy(header, 0, outToken, offset, header.length);477offset += header.length;478479// debug("WrapToken.encode: Writing data: [");480if (!privacy) {481482// debug(getHexBytes(confounder, confounder.length));483System.arraycopy(confounder, 0, outToken, offset,484confounder.length);485offset += confounder.length;486487// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));488System.arraycopy(dataBytes, dataOffset, outToken, offset,489dataLen);490offset += dataLen;491492// debug(" " + getHexBytes(padding, padding.length));493System.arraycopy(padding, 0, outToken, offset, padding.length);494495} else {496497cipherHelper.encryptData(this, confounder, dataBytes,498dataOffset, dataLen, padding, outToken, offset);499500// debug(getHexBytes(outToken, offset, dataSize));501}502503// debug("]\n");504505// %%% assume that plaintext length == ciphertext len506return (header.length + confounder.length + dataLen + padding.length);507508}509510protected int getKrb5TokenSize() throws GSSException {511return (getTokenSize() + dataSize);512}513514protected int getSealAlg(boolean conf, int qop) throws GSSException {515if (!conf) {516return SEAL_ALG_NONE;517}518519// ignore QOP520return cipherHelper.getSealAlg();521}522523// This implementation is way too conservative. And it certainly524// doesn't return the maximum limit.525static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,526CipherHelper ch) throws GSSException {527return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -528(getTokenSize(ch) + CONFOUNDER_SIZE) - 8); /* safety */529}530531}532533534