001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020
021package org.apache.directory.shared.kerberos.components;
022
023
024import java.nio.BufferOverflowException;
025import java.nio.ByteBuffer;
026import java.util.ArrayList;
027import java.util.List;
028
029import org.apache.directory.api.asn1.Asn1Object;
030import org.apache.directory.api.asn1.EncoderException;
031import org.apache.directory.api.asn1.ber.tlv.BerValue;
032import org.apache.directory.api.asn1.ber.tlv.TLV;
033import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
034import org.apache.directory.api.util.Strings;
035import org.apache.directory.server.i18n.I18n;
036import org.apache.directory.shared.kerberos.KerberosConstants;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040
041/**
042 * TYPED-DATA      ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
043 *         data-type       [0] Int32,
044 *         data-value      [1] OCTET STRING OPTIONAL
045 * }
046 *
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049public class TypedData implements Asn1Object
050{
051
052    // The inner class storing the individual TDs
053    public class TD
054    {
055        /** the type of TypedData */
056        private int dataType;
057
058        /** the TypedData data */
059        private byte[] dataValue;
060
061
062        /**
063         * @return the TD type
064         */
065        public int getDataType()
066        {
067            return dataType;
068        }
069
070
071        /**
072         * @return the TD data
073         */
074        public byte[] getDataValue()
075        {
076            return dataValue;
077        }
078    }
079
080    /** The list of TypedData elements */
081    private List<TD> typedDataList = new ArrayList<>();
082
083    /** The current TD being processed */
084    private TD currentTD;
085
086    /** The logger */
087    private static final Logger LOG = LoggerFactory.getLogger( TypedData.class );
088
089    /** Speedup for logs */
090    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
091
092    // Storage for computed lengths
093    private int dataTypeTagLength[];
094    private int dataValueTagLength[];
095    private int typedDataSeqLength[];
096    private int typedDataSeqSeqLength;
097
098
099    /**
100     * @return the currentTD type
101     */
102    public int getCurrentDataType()
103    {
104        return currentTD.dataType;
105    }
106
107
108    /**
109     * Set the current TD type
110     */
111    public void setCurrentDataType( int tdType )
112    {
113        currentTD.dataType = tdType;
114    }
115
116
117    /**
118     * @return the currentTD data
119     */
120    public byte[] getCurrentDataValue()
121    {
122        return currentTD.dataValue;
123    }
124
125
126    /**
127     * Set the current TD data
128     */
129    public void setCurrentDataValue( byte[] tdData )
130    {
131        currentTD.dataValue = tdData;
132    }
133
134
135    /**
136     * @return the currentTD
137     */
138    public TD getCurrentTD()
139    {
140        return currentTD;
141    }
142
143
144    /**
145     * Create a new currentTD
146     */
147    public void createNewTD()
148    {
149        currentTD = new TD();
150        typedDataList.add( currentTD );
151    }
152
153
154    /**
155     * @return the TypedData
156     */
157    public List<TD> getTypedData()
158    {
159        return typedDataList;
160    }
161
162
163    /**
164     * Compute the TypedData length
165     * <pre>
166     * 0x30 L1 TypedData sequence
167     *  |
168     *  +-- 0x30 L2 The TD sequence
169     *       |
170     *       +--&gt; 0xA0 L3 tdType tag
171     *       |     |
172     *       |     +--&gt; 0x02 L3-2 tdType (int)
173     *       |
174     *       +--&gt; [0xA1 L4 tdData tag
175     *             |
176     *             +--&gt; 0x04 L4-2 tdData (OCTET STRING)]
177     * </pre>
178     */
179    @Override
180    public int computeLength()
181    {
182        int i = 0;
183        typedDataSeqLength = new int[typedDataList.size()];
184        dataTypeTagLength = new int[typedDataList.size()];
185        dataValueTagLength = new int[typedDataList.size()];
186        typedDataSeqSeqLength = 0;
187
188        for ( TD td : typedDataList )
189        {
190            int adTypeLen = BerValue.getNbBytes( td.dataType );
191            dataTypeTagLength[i] = 1 + TLV.getNbBytes( adTypeLen ) + adTypeLen;
192            typedDataSeqLength[i] = 1 + TLV.getNbBytes( dataTypeTagLength[i] ) + dataTypeTagLength[i];
193
194            if ( td.dataValue != null )
195            {
196                dataValueTagLength[i] = 1 + TLV.getNbBytes( td.dataValue.length ) + td.dataValue.length;
197                typedDataSeqLength[i] += 1 + TLV.getNbBytes( dataValueTagLength[i] ) + dataValueTagLength[i];
198            }
199
200            typedDataSeqSeqLength += 1 + TLV.getNbBytes( typedDataSeqLength[i] ) + typedDataSeqLength[i];
201            i++;
202        }
203
204        return 1 + TLV.getNbBytes( typedDataSeqSeqLength ) + typedDataSeqSeqLength;
205    }
206
207
208    /**
209     * {@inheritDoc}
210     */
211    @Override
212    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
213    {
214        if ( buffer == null )
215        {
216            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
217        }
218
219        try
220        {
221            // The AuthorizationData SEQ OF Tag
222            buffer.put( UniversalTag.SEQUENCE.getValue() );
223            buffer.put( TLV.getBytes( typedDataSeqSeqLength ) );
224
225            int i = 0;
226            for ( TD td : typedDataList )
227            {
228                buffer.put( UniversalTag.SEQUENCE.getValue() );
229                buffer.put( TLV.getBytes( typedDataSeqLength[i] ) );
230
231                // the tdType
232                buffer.put( ( byte ) KerberosConstants.TYPED_DATA_TDTYPE_TAG );
233                buffer.put( TLV.getBytes( dataTypeTagLength[i] ) );
234                BerValue.encode( buffer, td.dataType );
235
236                if ( td.dataValue != null )
237                {
238                    // the tdData
239                    buffer.put( ( byte ) KerberosConstants.TYPED_DATA_TDDATA_TAG );
240                    buffer.put( TLV.getBytes( dataValueTagLength[i] ) );
241                    BerValue.encode( buffer, td.dataValue );
242                }
243
244                i++;
245            }
246        }
247        catch ( BufferOverflowException boe )
248        {
249            LOG.error( I18n.err( I18n.ERR_743_CANNOT_ENCODE_TYPED_DATA, 1 + TLV.getNbBytes( typedDataSeqSeqLength )
250                + typedDataSeqSeqLength, buffer.capacity() ) );
251            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
252        }
253
254        if ( IS_DEBUG )
255        {
256            LOG.debug( "TypedData encoding : {}", Strings.dumpBytes( buffer.array() ) );
257            LOG.debug( "TypedData initial value : {}", this );
258        }
259
260        return buffer;
261    }
262
263
264    /**
265     * @see Object#toString()
266     */
267    public String toString()
268    {
269        return toString( "" );
270    }
271
272
273    /**
274     * @see Object#toString()
275     */
276    public String toString( String tabs )
277    {
278        StringBuilder sb = new StringBuilder();
279
280        sb.append( tabs ).append( "TypedData : \n" );
281
282        for ( TD td : typedDataList )
283        {
284            sb.append( tabs ).append( "    {\n" );
285            sb.append( tabs ).append( "        tdType: " ).append( td.dataType ).append( '\n' );
286            if ( td.dataValue != null )
287            {
288                sb.append( tabs ).append( "        tdData: " ).append( Strings.dumpBytes( td.dataValue ) )
289                    .append( '\n' );
290            }
291            sb.append( tabs ).append( "    }\n" );
292        }
293
294        return sb.toString();
295    }
296}