View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.kerberos.shared.messages.value;
21  
22  
23  import java.nio.BufferOverflowException;
24  import java.nio.ByteBuffer;
25  
26  import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
27  import org.apache.directory.shared.asn1.AbstractAsn1Object;
28  import org.apache.directory.shared.asn1.ber.tlv.TLV;
29  import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
30  import org.apache.directory.shared.asn1.ber.tlv.Value;
31  import org.apache.directory.shared.asn1.codec.EncoderException;
32  import org.apache.directory.shared.ldap.util.StringTools;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  
37  /**
38   * A structure storing an encrypted data element. The ASN.1 grammar is :
39   * 
40   * EncryptedData   ::= SEQUENCE {
41   *        etype   [0] Int32 -- EncryptionType --,
42   *        kvno    [1] UInt32 OPTIONAL,
43   *        cipher  [2] OCTET STRING -- ciphertext
44   * }
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   * @version $Rev: 642496 $, $Date: 2008-03-29 04:09:22 +0100 (Sa, 29 Mär 2008) $
48   */
49  public class EncryptedData extends AbstractAsn1Object
50  {
51      /** The logger */
52      private static final Logger log = LoggerFactory.getLogger( EncryptedData.class );
53  
54      /** Speedup for logs */
55      private static final boolean IS_DEBUG = log.isDebugEnabled();
56  
57      /** The used encryption algorithm */
58      private EncryptionType eType;
59  
60      /** Version number of the key under which data is encrypted */
61      private int kvno;
62  
63      /** A flag used to tell if a kvno has been added, as the kvno is optional. */
64      private boolean hasKvno;
65  
66      /** The field containing the enciphered text */
67      private byte[] cipher;
68  
69      /** A constant used when the key is not present */
70      public static final boolean HAS_KVNO = true;
71  
72      // Storage for computed lengths
73      private transient int eTypeTagLength;
74      private transient int kvnoTagLength;
75      private transient int cipherTagLength;
76      private transient int encryptedDataSeqLength;
77  
78  
79      /**
80       * Creates a new instance of EncryptedData.
81       */
82      public EncryptedData()
83      {
84          hasKvno = !HAS_KVNO;
85      }
86      
87      /**
88       * Creates a new instance of EncryptedData.
89       *
90       * @param eType The encription algorithm
91       * @param kvno The key version
92       * @param cipher the encrypted text
93       */
94      public EncryptedData( EncryptionType eType, int kvno, byte[] cipher )
95      {
96          this.eType = eType;
97          this.hasKvno = kvno > 0;
98          this.kvno = kvno;
99          this.cipher = cipher;
100     }
101 
102 
103     /**
104      * Creates a new instance of EncryptedData.
105      *
106      * @param eType The encription algorithm
107      * @param cipher the encrypted text
108      */
109     public EncryptedData( EncryptionType eType, byte[] cipher )
110     {
111         this.eType = eType;
112         this.hasKvno = !HAS_KVNO;
113         kvno = -1;
114         this.cipher = cipher;
115     }
116 
117 
118     /**
119      * Returns the {@link EncryptionType}.
120      *
121      * @return The {@link EncryptionType}.
122      */
123     public EncryptionType getEType()
124     {
125         return eType;
126     }
127 
128 
129     /**
130      * Set the EncryptionType
131      * @param eType the EncryptionType
132      */
133     public void setEType( EncryptionType eType )
134     {
135         this.eType = eType;
136     }
137 
138     /**
139      * Returns the key version.
140      *
141      * @return The key version.
142      */
143     public int getKvno()
144     {
145         return hasKvno ? kvno : -1;
146     }
147 
148     /**
149      * Set the key version
150      * @param kvno The key version
151      */
152     public void setKvno( int kvno )
153     {
154         this.kvno = kvno;
155     }
156 
157     /**
158      * Tells if there is a key version.
159      *
160      * @return <code>true</code> if there is a key version.
161      */
162     public boolean hasKvno()
163     {
164         return hasKvno;
165     }
166 
167 
168     /**
169      * Returns the raw cipher text.
170      *
171      * @return The raw cipher text.
172      */
173     public byte[] getCipher()
174     {
175         return cipher;
176     }
177 
178     /**
179      * Set the cipher text
180      * @param cipher The cipher text
181      */
182     public void setCipher( byte[] cipher )
183     {
184         this.cipher = cipher;
185     }
186     
187 
188     /**
189      * Compute the EncryptedData length
190      * 
191      * EncryptedData :
192      * 
193      * 0x30 L1 EncryptedData sequence
194      *  |
195      *  +--> 0xA1 L2 etype tag
196      *  |     |
197      *  |     +--> 0x02 L2-1 etype (int)
198      *  |
199      *  +--> [0xA2 L3 kvno tag
200      *  |     |
201      *  |     +--> 0x30 L3-1 kvno (int)] (optional)
202      *  |
203      *  +--> 0xA2 L4 cipher tag
204      *        |
205      *        +--> 0x04 L4-1 cipher (OCTET STRING)
206      */
207     public int computeLength()
208     {
209         encryptedDataSeqLength = 0;
210 
211         // Compute the encryption Type length
212         int eTypeLength = Value.getNbBytes( eType.getOrdinal() );
213         eTypeTagLength = 1 + TLV.getNbBytes( eTypeLength ) + eTypeLength;
214         encryptedDataSeqLength = 1 + TLV.getNbBytes( eTypeTagLength ) + eTypeTagLength; 
215 
216 
217         // Compute the kvno length if any
218         if ( hasKvno )
219         {
220             int kvnoLength = Value.getNbBytes( kvno );
221             kvnoTagLength = 1 + TLV.getNbBytes( kvnoLength ) + kvnoLength;
222             encryptedDataSeqLength += 1 + TLV.getNbBytes( kvnoTagLength ) + kvnoTagLength;
223         }
224         else
225         {
226             kvnoTagLength = 0;
227         }
228 
229         // Compute the cipher
230         if ( ( cipher == null ) || ( cipher.length == 0 ) )
231         {
232             cipherTagLength = 1 + 1;
233         }
234         else
235         {
236             cipherTagLength = 1 + TLV.getNbBytes( cipher.length ) + cipher.length;
237         }
238 
239         encryptedDataSeqLength += 1 + TLV.getNbBytes( cipherTagLength ) + cipherTagLength;
240 
241         // Compute the whole sequence length
242         return 1 + TLV.getNbBytes( encryptedDataSeqLength ) + encryptedDataSeqLength;
243     }
244 
245 
246     /**
247      * Encode the EncryptedData message to a PDU. 
248      * 
249      * EncryptedData :
250      * 
251      * 0x30 LL
252      *   0xA0 LL 
253      *     0x02 0x01 etype (integer)
254      *   [0xA1 LL 
255      *     0x02 0x01 kvno (integer)] (optional)
256      *   0xA2 LL 
257      *     0x04 LL cipher (OCTET STRING)
258      * 
259      * @param buffer The buffer where to put the PDU. It should have been allocated
260      * before, with the right size.
261      * @return The constructed PDU.
262      */
263     public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
264     {
265         if ( buffer == null )
266         {
267             throw new EncoderException( "Cannot put a PDU in a null buffer !" );
268         }
269 
270         try
271         {
272             // The EncryptedData SEQ Tag
273             buffer.put( UniversalTag.SEQUENCE_TAG );
274             buffer.put( TLV.getBytes( encryptedDataSeqLength ) );
275 
276             // The etype, first the tag, then the value
277             buffer.put( ( byte ) 0xA0 );
278             buffer.put( TLV.getBytes( eTypeTagLength ) );
279 
280             Value.encode( buffer, eType.getOrdinal() );
281 
282             // The kvno, if any, first the tag, then the value
283             if ( hasKvno )
284             {
285                 buffer.put( ( byte ) 0xA1 );
286                 buffer.put( TLV.getBytes( kvnoTagLength ) );
287 
288                 Value.encode( buffer, kvno );
289             }
290 
291             // The cipher tag
292             buffer.put( ( byte ) 0xA2 );
293             buffer.put( TLV.getBytes( cipherTagLength ) );
294             Value.encode( buffer, cipher );
295         }
296         catch ( BufferOverflowException boe )
297         {
298             log.error(
299                 "Cannot encode the EncryptedData object, the PDU size is {} when only {} bytes has been allocated", 1
300                     + TLV.getNbBytes( encryptedDataSeqLength ) + encryptedDataSeqLength, buffer.capacity() );
301             throw new EncoderException( "The PDU buffer size is too small !" );
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 }