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;
025
026import org.apache.directory.api.asn1.Asn1Object;
027import org.apache.directory.api.asn1.EncoderException;
028import org.apache.directory.api.asn1.ber.tlv.BerValue;
029import org.apache.directory.api.asn1.ber.tlv.TLV;
030import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
031import org.apache.directory.api.util.Strings;
032import org.apache.directory.server.i18n.I18n;
033import org.apache.directory.shared.kerberos.KerberosConstants;
034import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038
039/**
040 * Provides encryption info information sent to the client.
041 * 
042 * The ASN.1 grammar for this structure is :
043 * <pre>
044 * ETYPE-INFO2-ENTRY        ::= SEQUENCE {
045 *            etype           [0] Int32,
046 *            salt            [1] KerberosString OPTIONAL,
047 *            s2kparams       [2] OCTET STRING OPTIONAL
048 *    }
049 * </pre>
050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051 */
052public class ETypeInfo2Entry implements Asn1Object
053{
054    /** The logger */
055    private static final Logger LOG = LoggerFactory.getLogger( ETypeInfo2Entry.class );
056
057    /** Speedup for logs */
058    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
059
060    /** The encryption type */
061    private EncryptionType etype;
062
063    /** The salt */
064    private String salt;
065    private byte[] saltBytes;
066
067    /** The s2k params */
068    private byte[] s2kparams;
069
070    // Storage for computed lengths
071    private int etypeTagLength;
072    private int saltTagLength;
073    private int s2kparamsTagLength;
074    private int etypeInfo2EntrySeqLength;
075
076
077    /**
078     * Creates a new instance of ETypeInfo2Entry.
079     */
080    public ETypeInfo2Entry()
081    {
082    }
083
084
085    public ETypeInfo2Entry( EncryptionType etype )
086    {
087        this.etype = etype;
088    }
089
090
091    /**
092     * Returns the salt.
093     *
094     * @return The salt.
095     */
096    public String getSalt()
097    {
098        return salt;
099    }
100
101
102    /**
103     * @param salt the salt to set
104     */
105    public void setSalt( String salt )
106    {
107        this.salt = salt;
108    }
109
110
111    /**
112     * Returns the s2kparams.
113     *
114     * @return The s2kparams.
115     */
116    public byte[] getS2kparams()
117    {
118        return s2kparams;
119    }
120
121
122    /**
123     * @param s2kparams the s2kparams to set
124     */
125    public void setS2kparams( byte[] s2kparams )
126    {
127        this.s2kparams = s2kparams;
128    }
129
130
131    /**
132     * Returns the {@link EncryptionType}.
133     *
134     * @return The {@link EncryptionType}.
135     */
136    public EncryptionType getEType()
137    {
138        return etype;
139    }
140
141
142    /**
143     * @param etype the encryptionType to set
144     */
145    public void setEType( EncryptionType etype )
146    {
147        this.etype = etype;
148    }
149
150
151    /**
152     * Compute the ETYPE-INFO2-ENTRY length
153     * <pre>
154     * ETYPE-INFO-ENTRY :
155     * 
156     * 0x30 L1 ETYPE-INFO2-ENTRY sequence
157     *  |
158     *  +--&gt; 0xA0 L2 etype tag
159     *  |     |
160     *  |     +--&gt; 0x02 L2-1etype (int)
161     *  |
162     *  +--&gt; 0xA1 L3 salt tag
163     *  |     |
164     *  |     +--&gt; 0x1B L3-1 salt (KerberosString)
165     *  |
166     *  +--&gt; 0xA2 L4 s2kparams tag
167     *        |
168     *        +--&gt; 0x04 L4-1 salt (OCTET STRING)
169     *        
170     *  where L1 = L2 + length(0xA0) + length(L2) +
171     *             L3 + length(0xA1) + length(L3) +
172     *             L4 + length(0xA2) + length( L4)
173     *  and
174     *  L2 = L2-1 + length(0x02) + length( L2-1) 
175     *  L3 = L3-1 + length(0x1B) + length( L3-1) 
176     *  L4 = L4-1 + length(0x04) + length( L4-1) 
177     *  </pre>
178     */
179    public int computeLength()
180    {
181        // Compute the etype. The Length will always be contained in 1 byte
182        int etypeLength = BerValue.getNbBytes( etype.getValue() );
183        etypeTagLength = 1 + TLV.getNbBytes( etypeLength ) + etypeLength;
184        etypeInfo2EntrySeqLength = 1 + TLV.getNbBytes( etypeTagLength ) + etypeTagLength;
185
186        // Compute the salt
187        if ( salt != null )
188        {
189            saltBytes = Strings.getBytesUtf8( salt );
190            saltTagLength = 1 + TLV.getNbBytes( saltBytes.length ) + saltBytes.length;
191            etypeInfo2EntrySeqLength += 1 + TLV.getNbBytes( saltTagLength ) + saltTagLength;
192        }
193
194        // Compute the s2kparams
195        if ( s2kparams != null )
196        {
197            s2kparamsTagLength = 1 + TLV.getNbBytes( s2kparams.length ) + s2kparams.length;
198            etypeInfo2EntrySeqLength += 1 + TLV.getNbBytes( s2kparamsTagLength ) + s2kparamsTagLength;
199        }
200
201        return 1 + TLV.getNbBytes( etypeInfo2EntrySeqLength ) + etypeInfo2EntrySeqLength;
202    }
203
204
205    /**
206     * Encode the ETYPE-INFO2-ENTRY message to a PDU. 
207     * <pre>
208     * ETYPE-INFO2-ENTRY :
209     * 
210     * 0x30 LL
211     *   0xA0 LL 
212     *     0x02 0x01 etype
213     *   0xA1 LL 
214     *     0x1B LL salt
215     *   0xA2 LL 
216     *     0x04 LL s2kparams
217     * </pre>
218     * @param buffer The buffer where to put the PDU. It should have been allocated
219     * before, with the right size.
220     * @return The constructed PDU.
221     */
222    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
223    {
224        if ( buffer == null )
225        {
226            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
227        }
228
229        try
230        {
231            // The ETYPE-INFO2-ENTRY SEQ Tag
232            buffer.put( UniversalTag.SEQUENCE.getValue() );
233            buffer.put( TLV.getBytes( etypeInfo2EntrySeqLength ) );
234
235            // The etype, first the tag, then the value
236            buffer.put( ( byte ) KerberosConstants.ETYPE_INFO2_ENTRY_ETYPE_TAG );
237            buffer.put( TLV.getBytes( etypeTagLength ) );
238            BerValue.encode( buffer, etype.getValue() );
239
240            // The salt, first the tag, then the value, if salt is not null
241            if ( salt != null )
242            {
243                // The tag
244                buffer.put( ( byte ) KerberosConstants.ETYPE_INFO2_ENTRY_SALT_TAG );
245                buffer.put( TLV.getBytes( saltTagLength ) );
246
247                // The value
248                buffer.put( UniversalTag.GENERAL_STRING.getValue() );
249                buffer.put( TLV.getBytes( saltBytes.length ) );
250                buffer.put( saltBytes );
251            }
252
253            // The s2kparams, first the tag, then the value, if s2kparams is not null
254            if ( s2kparams != null )
255            {
256                buffer.put( ( byte ) KerberosConstants.ETYPE_INFO2_ENTRY_S2KPARAMS_TAG );
257                buffer.put( TLV.getBytes( saltTagLength ) );
258                BerValue.encode( buffer, s2kparams );
259            }
260        }
261        catch ( BufferOverflowException boe )
262        {
263            LOG.error( I18n.err( I18n.ERR_145, 1 + TLV.getNbBytes( etypeInfo2EntrySeqLength )
264                + etypeInfo2EntrySeqLength, buffer.capacity() ) );
265            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
266        }
267
268        if ( IS_DEBUG )
269        {
270            LOG.debug( "ETYPE-INFO2-ENTRY encoding : {}", Strings.dumpBytes( buffer.array() ) );
271            LOG.debug( "ETYPE-INFO2-ENTRY initial value : {}", this );
272        }
273
274        return buffer;
275    }
276
277
278    /**
279     * @see Object#toString()
280     */
281    public String toString()
282    {
283        StringBuilder sb = new StringBuilder();
284
285        sb.append( "ETYPE-INFO2-ENTRY : {\n" );
286        sb.append( "    etype: " ).append( etype ).append( '\n' );
287
288        if ( salt != null )
289        {
290            sb.append( "    salt: " ).append( salt ).append( '\n' );
291        }
292
293        if ( salt != null )
294        {
295            sb.append( "    s2kparams: " ).append( Strings.dumpBytes( s2kparams ) ).append( '\n' );
296        }
297
298        sb.append( "}\n" );
299
300        return sb.toString();
301    }
302}