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    
026    import org.apache.directory.server.i18n.I18n;
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 structure storing an encrypted data element. The ASN.1 grammar is :
040     * 
041     * EncryptedData   ::= SEQUENCE {
042     *        etype   [0] Int32 -- EncryptionType --,
043     *        kvno    [1] UInt32 OPTIONAL,
044     *        cipher  [2] OCTET STRING -- ciphertext
045     * }
046     *
047     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048     * @version $Rev: 902575 $, $Date: 2010-01-24 15:38:06 +0100 (Sun, 24 Jan 2010) $
049     */
050    public class EncryptedData extends AbstractAsn1Object
051    {
052        /** The logger */
053        private static final Logger log = LoggerFactory.getLogger( EncryptedData.class );
054    
055        /** Speedup for logs */
056        private static final boolean IS_DEBUG = log.isDebugEnabled();
057    
058        /** The used encryption algorithm */
059        private EncryptionType eType;
060    
061        /** Version number of the key under which data is encrypted */
062        private int kvno;
063    
064        /** A flag used to tell if a kvno has been added, as the kvno is optional. */
065        private boolean hasKvno;
066    
067        /** The field containing the enciphered text */
068        private byte[] cipher;
069    
070        /** A constant used when the key is not present */
071        public static final boolean HAS_KVNO = true;
072    
073        // Storage for computed lengths
074        private transient int eTypeTagLength;
075        private transient int kvnoTagLength;
076        private transient int cipherTagLength;
077        private transient int encryptedDataSeqLength;
078    
079    
080        /**
081         * Creates a new instance of EncryptedData.
082         */
083        public EncryptedData()
084        {
085            hasKvno = !HAS_KVNO;
086        }
087        
088        /**
089         * Creates a new instance of EncryptedData.
090         *
091         * @param eType The encription algorithm
092         * @param kvno The key version
093         * @param cipher the encrypted text
094         */
095        public EncryptedData( EncryptionType eType, int kvno, byte[] cipher )
096        {
097            this.eType = eType;
098            this.hasKvno = kvno > 0;
099            this.kvno = kvno;
100            this.cipher = cipher;
101        }
102    
103    
104        /**
105         * Creates a new instance of EncryptedData.
106         *
107         * @param eType The encription algorithm
108         * @param cipher the encrypted text
109         */
110        public EncryptedData( EncryptionType eType, byte[] cipher )
111        {
112            this.eType = eType;
113            this.hasKvno = !HAS_KVNO;
114            kvno = -1;
115            this.cipher = cipher;
116        }
117    
118    
119        /**
120         * Returns the {@link EncryptionType}.
121         *
122         * @return The {@link EncryptionType}.
123         */
124        public EncryptionType getEType()
125        {
126            return eType;
127        }
128    
129    
130        /**
131         * Set the EncryptionType
132         * @param eType the EncryptionType
133         */
134        public void setEType( EncryptionType eType )
135        {
136            this.eType = eType;
137        }
138    
139        /**
140         * Returns the key version.
141         *
142         * @return The key version.
143         */
144        public int getKvno()
145        {
146            return hasKvno ? kvno : -1;
147        }
148    
149        /**
150         * Set the key version
151         * @param kvno The key version
152         */
153        public void setKvno( int kvno )
154        {
155            this.kvno = kvno;
156        }
157    
158        /**
159         * Tells if there is a key version.
160         *
161         * @return <code>true</code> if there is a key version.
162         */
163        public boolean hasKvno()
164        {
165            return hasKvno;
166        }
167    
168    
169        /**
170         * Returns the raw cipher text.
171         *
172         * @return The raw cipher text.
173         */
174        public byte[] getCipher()
175        {
176            return cipher;
177        }
178    
179        /**
180         * Set the cipher text
181         * @param cipher The cipher text
182         */
183        public void setCipher( byte[] cipher )
184        {
185            this.cipher = cipher;
186        }
187        
188    
189        /**
190         * Compute the EncryptedData length
191         * 
192         * EncryptedData :
193         * 
194         * 0x30 L1 EncryptedData sequence
195         *  |
196         *  +--> 0xA1 L2 etype tag
197         *  |     |
198         *  |     +--> 0x02 L2-1 etype (int)
199         *  |
200         *  +--> [0xA2 L3 kvno tag
201         *  |     |
202         *  |     +--> 0x30 L3-1 kvno (int)] (optional)
203         *  |
204         *  +--> 0xA2 L4 cipher tag
205         *        |
206         *        +--> 0x04 L4-1 cipher (OCTET STRING)
207         */
208        public int computeLength()
209        {
210            encryptedDataSeqLength = 0;
211    
212            // Compute the encryption Type length
213            int eTypeLength = Value.getNbBytes( eType.getOrdinal() );
214            eTypeTagLength = 1 + TLV.getNbBytes( eTypeLength ) + eTypeLength;
215            encryptedDataSeqLength = 1 + TLV.getNbBytes( eTypeTagLength ) + eTypeTagLength; 
216    
217    
218            // Compute the kvno length if any
219            if ( hasKvno )
220            {
221                int kvnoLength = Value.getNbBytes( kvno );
222                kvnoTagLength = 1 + TLV.getNbBytes( kvnoLength ) + kvnoLength;
223                encryptedDataSeqLength += 1 + TLV.getNbBytes( kvnoTagLength ) + kvnoTagLength;
224            }
225            else
226            {
227                kvnoTagLength = 0;
228            }
229    
230            // Compute the cipher
231            if ( ( cipher == null ) || ( cipher.length == 0 ) )
232            {
233                cipherTagLength = 1 + 1;
234            }
235            else
236            {
237                cipherTagLength = 1 + TLV.getNbBytes( cipher.length ) + cipher.length;
238            }
239    
240            encryptedDataSeqLength += 1 + TLV.getNbBytes( cipherTagLength ) + cipherTagLength;
241    
242            // Compute the whole sequence length
243            return 1 + TLV.getNbBytes( encryptedDataSeqLength ) + encryptedDataSeqLength;
244        }
245    
246    
247        /**
248         * Encode the EncryptedData message to a PDU. 
249         * 
250         * EncryptedData :
251         * 
252         * 0x30 LL
253         *   0xA0 LL 
254         *     0x02 0x01 etype (integer)
255         *   [0xA1 LL 
256         *     0x02 0x01 kvno (integer)] (optional)
257         *   0xA2 LL 
258         *     0x04 LL cipher (OCTET STRING)
259         * 
260         * @param buffer The buffer where to put the PDU. It should have been allocated
261         * before, with the right size.
262         * @return The constructed PDU.
263         */
264        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
265        {
266            if ( buffer == null )
267            {
268                throw new EncoderException( I18n.err( I18n.ERR_148 ) );
269            }
270    
271            try
272            {
273                // The EncryptedData SEQ Tag
274                buffer.put( UniversalTag.SEQUENCE_TAG );
275                buffer.put( TLV.getBytes( encryptedDataSeqLength ) );
276    
277                // The etype, first the tag, then the value
278                buffer.put( ( byte ) 0xA0 );
279                buffer.put( TLV.getBytes( eTypeTagLength ) );
280    
281                Value.encode( buffer, eType.getOrdinal() );
282    
283                // The kvno, if any, first the tag, then the value
284                if ( hasKvno )
285                {
286                    buffer.put( ( byte ) 0xA1 );
287                    buffer.put( TLV.getBytes( kvnoTagLength ) );
288    
289                    Value.encode( buffer, kvno );
290                }
291    
292                // The cipher tag
293                buffer.put( ( byte ) 0xA2 );
294                buffer.put( TLV.getBytes( cipherTagLength ) );
295                Value.encode( buffer, cipher );
296            }
297            catch ( BufferOverflowException boe )
298            {
299                log.error( I18n.err( I18n.ERR_141, 1 + TLV.getNbBytes( encryptedDataSeqLength ) 
300                    + encryptedDataSeqLength, buffer.capacity() ) );
301                throw new EncoderException( I18n.err( I18n.ERR_138 ) );
302            }
303    
304            if ( IS_DEBUG )
305            {
306                log.debug( "EncryptedData encoding : {}", StringTools.dumpBytes( buffer.array() ) );
307                log.debug( "EncryptedData initial value : {}", toString() );
308            }
309    
310            return buffer;
311        }
312    
313    
314        /**
315         * @see Object#toString()
316         */
317        public String toString()
318        {
319            StringBuilder sb = new StringBuilder();
320    
321            sb.append( "EncryptedData : {\n" );
322            sb.append( "    etype: " ).append( eType ).append( '\n' );
323    
324            if ( hasKvno )
325            {
326                sb.append( "    kvno: " ).append( kvno ).append( '\n' );
327            }
328    
329            sb.append( "    cipher: " ).append( StringTools.dumpBytes( cipher ) ).append( "\n}\n" );
330    
331            return sb.toString();
332        }
333    }