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 */
020package org.apache.directory.shared.kerberos.components;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025import java.util.Arrays;
026
027import org.apache.directory.api.asn1.Asn1Object;
028import org.apache.directory.api.asn1.EncoderException;
029import org.apache.directory.api.asn1.ber.tlv.BerValue;
030import org.apache.directory.api.asn1.ber.tlv.TLV;
031import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
032import org.apache.directory.api.util.Strings;
033import org.apache.directory.server.i18n.I18n;
034import org.apache.directory.shared.kerberos.KerberosConstants;
035import org.apache.directory.shared.kerberos.codec.types.TransitedEncodingType;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039
040/**
041 * The TransitedEncoding structure.
042 * 
043 * The ASN.1 grammar is :
044 * <pre>
045 * -- encoded Transited field
046 * TransitedEncoding       ::= SEQUENCE {
047 *         tr-type         [0] Int32 -- must be registered --,
048 *         contents        [1] OCTET STRING
049 * }
050 * </pre>
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public class TransitedEncoding implements Asn1Object
054{
055    /** The logger */
056    private static final Logger log = LoggerFactory.getLogger( TransitedEncoding.class );
057
058    /** Speedup for logs */
059    private static final boolean IS_DEBUG = log.isDebugEnabled();
060
061    /** 
062     * The transited type. One of :
063     * NULL
064     * DOMAIN_X500_COMPRESS
065     */
066    private TransitedEncodingType trType;
067
068    /** The transited data */
069    private byte[] contents;
070
071    // Storage for computed lengths
072    private int trTypeLength;
073    private int contentsLength;
074    private int transitedEncodingLength;
075
076
077    /**
078     * Creates a new instance of TransitedEncoding.
079     */
080    public TransitedEncoding()
081    {
082        trType = TransitedEncodingType.NULL;
083        contents = Strings.EMPTY_BYTES;
084    }
085
086
087    /**
088     * Returns the contents.
089     *
090     * @return The contents.
091     */
092    public byte[] getContents()
093    {
094        return contents;
095    }
096
097
098    /**
099     * Set the contents
100     * @param contents The contents
101     */
102    public void setContents( byte[] contents )
103    {
104        this.contents = contents;
105    }
106
107
108    /**
109     * Returns the {@link TransitedEncodingType}.
110     *
111     * @return The {@link TransitedEncodingType}.
112     */
113    public TransitedEncodingType getTrType()
114    {
115        return trType;
116    }
117
118
119    /**
120     * Set the transited encoding type
121     * @param trType The transited encoding type
122     */
123    public void setTrType( TransitedEncodingType trType )
124    {
125        this.trType = trType;
126    }
127
128
129    /**
130     * Compute the TransitedEncoding length
131     * 
132     * <pre>
133     * TransitedEncoding :
134     * 
135     * 0x30 L1 TransitedEncoding
136     *  |
137     *  +--&gt; 0xA0 L2 trType tag
138     *  |     |
139     *  |     +--&gt; 0x02 L2-1 trType (int)
140     *  |
141     *  +--&gt; 0xA1 L3 contents tag
142     *        |
143     *        +--&gt; 0x04 L3-1 contents (OCTET STRING)
144     *        
145     *  where L1 = L2 + lenght(0xA0) + length(L2) +
146     *             L3 + lenght(0xA1) + length(L3) 
147     *  and
148     *  L2 = L2-1 + length(0x02) + length( L2-1) 
149     *  L3 = L3-1 + length(0x04) + length( L3-1) 
150     *  </pre>
151     */
152    public int computeLength()
153    {
154        // Compute the trType. The Length will always be contained in 1 byte
155        trTypeLength = 1 + 1 + BerValue.getNbBytes( trType.getValue() );
156        transitedEncodingLength = 1 + TLV.getNbBytes( trTypeLength ) + trTypeLength;
157
158        // Compute the contents length
159        if ( contents == null )
160        {
161            contentsLength = 1 + 1;
162        }
163        else
164        {
165            contentsLength = 1 + TLV.getNbBytes( contents.length ) + contents.length;
166        }
167
168        transitedEncodingLength += 1 + TLV.getNbBytes( contentsLength ) + contentsLength;
169
170        // Compute the whole sequence length
171        return 1 + TLV.getNbBytes( transitedEncodingLength ) + transitedEncodingLength;
172    }
173
174
175    /**
176     * Encode the TransitedEncoding message to a PDU. 
177     * 
178     * TransitedEncoding :
179     * 
180     * 0x30 LL
181     *   0xA0 LL 
182     *     0x02 0x01 trType
183     *   0xA1 LL 
184     *     0x04 LL contents
185     * 
186     * @param buffer The buffer where to put the PDU. It should have been allocated
187     * before, with the right size.
188     * @return The constructed PDU.
189     */
190    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
191    {
192        if ( buffer == null )
193        {
194            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
195        }
196
197        try
198        {
199            // The AuthorizationDataEntry SEQ Tag
200            buffer.put( UniversalTag.SEQUENCE.getValue() );
201            buffer.put( TLV.getBytes( transitedEncodingLength ) );
202
203            // The tr-type, first the tag, then the value
204            buffer.put( ( byte ) KerberosConstants.TRANSITED_ENCODING_TR_TYPE_TAG );
205            buffer.put( TLV.getBytes( trTypeLength ) );
206            BerValue.encode( buffer, trType.getValue() );
207
208            // The contents, first the tag, then the value
209            buffer.put( ( byte ) KerberosConstants.TRANSITED_ENCODING_CONTENTS_TAG );
210            buffer.put( TLV.getBytes( contentsLength ) );
211            BerValue.encode( buffer, contents );
212        }
213        catch ( BufferOverflowException boe )
214        {
215            log.error( I18n.err( I18n.ERR_147, 1 + TLV.getNbBytes( transitedEncodingLength )
216                + transitedEncodingLength, buffer.capacity() ) );
217            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
218        }
219
220        if ( IS_DEBUG )
221        {
222            log.debug( "TransitedEncoding encoding : {}", Strings.dumpBytes( buffer.array() ) );
223            log.debug( "TransitedEncoding initial value : {}", this );
224        }
225
226        return buffer;
227    }
228
229
230    /**
231     * {@inheritDoc}
232     */
233    @Override
234    public int hashCode()
235    {
236        final int prime = 31;
237        int result = 1;
238        result = prime * result + Arrays.hashCode( contents );
239        result = prime * result + ( ( trType == null ) ? 0 : trType.hashCode() );
240        return result;
241    }
242
243
244    /**
245     * {@inheritDoc}
246     */
247    @Override
248    public boolean equals( Object obj )
249    {
250        if ( this == obj )
251        {
252            return true;
253        }
254
255        if ( !( obj instanceof TransitedEncoding ) )
256        {
257            return false;
258        }
259
260        TransitedEncoding other = ( TransitedEncoding ) obj;
261
262        if ( !Arrays.equals( contents, other.contents ) )
263        {
264            return false;
265        }
266
267        return trType == other.trType;
268    }
269
270
271    /**
272     * @see Object#toString()
273     */
274    public String toString()
275    {
276        StringBuilder sb = new StringBuilder();
277
278        sb.append( "TransitedEncoding : {\n" );
279        sb.append( "    tr-type: " ).append( trType ).append( '\n' );
280
281        sb.append( "    contents: " ).append( Strings.dumpBytes( contents ) ).append( "\n}\n" );
282
283        return sb.toString();
284    }
285}