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