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.security.MessageDigest;
026import java.util.Arrays;
027
028import org.apache.directory.api.asn1.Asn1Object;
029import org.apache.directory.api.asn1.EncoderException;
030import org.apache.directory.api.asn1.ber.tlv.BerValue;
031import org.apache.directory.api.asn1.ber.tlv.TLV;
032import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
033import org.apache.directory.api.util.Strings;
034import org.apache.directory.server.i18n.I18n;
035import org.apache.directory.shared.kerberos.KerberosConstants;
036import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040
041/**
042 * A Kerberos symmetric encryption key, which includes metadata support for
043 * the associated key type and key version number.
044 * 
045 * The ASN.1 description for this structure is :
046 * <pre>
047 * EncryptionKey   ::= SEQUENCE {
048 *       keytype         [0] Int32 -- actually encryption type --,
049 *       keyvalue        [1] OCTET STRING
050 * }
051 * </pre>
052 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
053 */
054public class EncryptionKey implements Asn1Object
055{
056    /** The logger */
057    private static final Logger log = LoggerFactory.getLogger( EncryptionKey.class );
058
059    /** Speedup for logs */
060    private static final boolean IS_DEBUG = log.isDebugEnabled();
061
062    /** The encryption type */
063    private EncryptionType keyType;
064
065    /** The encrypted value */
066    private byte[] keyValue;
067
068    /** The key version */
069    private int keyVersion;
070
071    // Storage for computed lengths
072    private int keyTypeLength;
073    private int keyValueLength;
074    private int encryptionKeyLength;
075
076
077    /**
078     * Creates a new instance of EncryptionKey.
079     */
080    public EncryptionKey()
081    {
082    }
083
084
085    /**
086     * Creates a new instance of EncryptionKey.
087     *
088     * @param keyType The encryptionType 
089     * @param keyValue The value
090     */
091    public EncryptionKey( EncryptionType keyType, byte[] keyValue )
092    {
093        this.keyType = keyType;
094        this.keyValue = keyValue;
095    }
096
097
098    /**
099     * Creates a new instance of EncryptionKey.
100     *
101     * @param keyType The encryptionType 
102     * @param keyValue The value
103     * @param keyVersion ???
104     */
105    public EncryptionKey( EncryptionType keyType, byte[] keyValue, int keyVersion )
106    {
107        this.keyType = keyType;
108        this.keyValue = keyValue;
109        this.keyVersion = keyVersion;
110    }
111
112
113    /**
114     * Destroys this key by overwriting the symmetric key material with zeros.
115     */
116    public synchronized void destroy()
117    {
118        if ( keyValue != null )
119        {
120            Arrays.fill( keyValue, ( byte ) 0x00 );
121        }
122    }
123
124
125    /**
126     * Returns the key type.
127     *
128     * @return The key type.
129     */
130    public EncryptionType getKeyType()
131    {
132        return keyType;
133    }
134
135
136    /**
137     * Set the encryption type
138     * @param keyType The encryption type
139     */
140    public void setKeyType( EncryptionType keyType )
141    {
142        this.keyType = keyType;
143    }
144
145
146    /**
147     * Returns the key value.
148     *
149     * @return The key value.
150     */
151    public byte[] getKeyValue()
152    {
153        return keyValue;
154    }
155
156
157    /**
158     * Returns the key version.
159     *
160     * @return The key version.
161     */
162    public int getKeyVersion()
163    {
164        return keyVersion;
165    }
166
167
168    /**
169     * Set the key value
170     * @param keyVersion The key version
171     */
172    public void setKeyVersion( int keyVersion )
173    {
174        this.keyVersion = keyVersion;
175    }
176
177
178    /**
179     * Set the key value
180     * @param keyValue The key value
181     */
182    public void setKeyValue( byte[] keyValue )
183    {
184        this.keyValue = keyValue;
185    }
186
187
188    /**
189     * {@inheritDoc}
190     */
191    @Override
192    public int hashCode()
193    {
194        int hash = 37;
195        hash = hash * 17 + keyType.hashCode();
196        hash = hash * 17 + Arrays.hashCode( keyValue );
197
198        return hash;
199    }
200
201
202    /**
203     * @see Object#equals(Object)
204     */
205    @Override
206    public boolean equals( Object o )
207    {
208        if ( this == o )
209        {
210            return true;
211        }
212
213        if ( !( o instanceof EncryptionKey ) )
214        {
215            return false;
216        }
217
218        EncryptionKey that = ( EncryptionKey ) o;
219        return ( this.keyType == that.keyType ) && ( MessageDigest.isEqual( this.keyValue, that.keyValue ) );
220    }
221
222
223    /**
224     * Compute the EncryptionKey length
225     * <pre>
226     * EncryptionKey :
227     * 
228     * 0x30 L1 EncryptionKey
229     *  |
230     *  +--&gt; 0xA0 L2 keyType tag
231     *  |     |
232     *  |     +--&gt; 0x02 L2-1 keyType (int)
233     *  |
234     *  +--&gt; 0xA1 L3 keyValue tag
235     *        |
236     *        +--&gt; 0x04 L3-1 keyValue (OCTET STRING)
237     *        
238     *  where L1 = L2 + lenght(0xA0) + length(L2) +
239     *             L3 + lenght(0xA1) + length(L3) 
240     *  and
241     *  L2 = L2-1 + length(0x02) + length( L2-1) 
242     *  L3 = L3-1 + length(0x04) + length( L3-1)
243     *  </pre> 
244     */
245    public int computeLength()
246    {
247        // Compute the keyType. The Length will always be cobntained in 1 byte
248        keyTypeLength = 1 + 1 + BerValue.getNbBytes( keyType.getValue() );
249        encryptionKeyLength = 1 + TLV.getNbBytes( keyTypeLength ) + keyTypeLength;
250
251        // Compute the keyValue
252        if ( keyValue == null )
253        {
254            keyValueLength = 1 + 1;
255        }
256        else
257        {
258            keyValueLength = 1 + TLV.getNbBytes( keyValue.length ) + keyValue.length;
259        }
260
261        encryptionKeyLength += 1 + TLV.getNbBytes( keyValueLength ) + keyValueLength;
262
263        // Compute the whole sequence length
264        return 1 + BerValue.getNbBytes( encryptionKeyLength ) + encryptionKeyLength;
265    }
266
267
268    /**
269     * Encode the EncryptionKey message to a PDU. 
270     * <pre>
271     * EncryptionKey :
272     * 
273     * 0x30 LL
274     *   0xA0 LL 
275     *     0x02 0x01 keyType
276     *   0xA1 LL 
277     *     0x04 LL keyValue
278     * </pre>
279     * @param buffer The buffer where to put the PDU. It should have been allocated
280     * before, with the right size.
281     * @return The constructed PDU.
282     */
283    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
284    {
285        if ( buffer == null )
286        {
287            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
288        }
289
290        try
291        {
292            // The EncryptionKey SEQ Tag
293            buffer.put( UniversalTag.SEQUENCE.getValue() );
294            buffer.put( TLV.getBytes( encryptionKeyLength ) );
295
296            // The keyType, first the tag, then the value
297            buffer.put( ( byte ) KerberosConstants.ENCRYPTION_KEY_TYPE_TAG );
298            buffer.put( TLV.getBytes( keyTypeLength ) );
299            BerValue.encode( buffer, keyType.getValue() );
300
301            // The keyValue, first the tag, then the value
302            buffer.put( ( byte ) KerberosConstants.ENCRYPTION_KEY_VALUE_TAG );
303            buffer.put( TLV.getBytes( keyValueLength ) );
304            BerValue.encode( buffer, keyValue );
305        }
306        catch ( BufferOverflowException boe )
307        {
308            log.error( I18n.err( I18n.ERR_142, 1 + TLV.getNbBytes( encryptionKeyLength )
309                + encryptionKeyLength, buffer.capacity() ) );
310            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
311        }
312
313        if ( IS_DEBUG )
314        {
315            log.debug( "EncryptionKey encoding : {}", Strings.dumpBytes( buffer.array() ) );
316            log.debug( "EncryptionKey initial value : {}", this );
317        }
318
319        return buffer;
320    }
321
322
323    /**
324     * @see Object#toString()
325     */
326    public String toString()
327    {
328        return keyType.toString() + " (" + keyType.getValue() + ")";
329    }
330}