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.shared.kerberos.components;
21  
22  
23  import java.nio.BufferOverflowException;
24  import java.nio.ByteBuffer;
25  import java.security.MessageDigest;
26  import java.util.Arrays;
27  
28  import org.apache.directory.api.asn1.Asn1Object;
29  import org.apache.directory.api.asn1.EncoderException;
30  import org.apache.directory.api.asn1.ber.tlv.BerValue;
31  import org.apache.directory.api.asn1.ber.tlv.TLV;
32  import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
33  import org.apache.directory.api.util.Strings;
34  import org.apache.directory.server.i18n.I18n;
35  import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  
40  /**
41   * A structure storing an encrypted data element. The ASN.1 grammar is :
42   * <pre>
43   * EncryptedData   ::= SEQUENCE {
44   *        etype   [0] Int32 -- EncryptionType --,
45   *        kvno    [1] UInt32 OPTIONAL,
46   *        cipher  [2] OCTET STRING -- ciphertext
47   * }
48   *</pre>
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   */
51  public class EncryptedData implements Asn1Object
52  {
53      /** The logger */
54      private static final Logger log = LoggerFactory.getLogger( EncryptedData.class );
55  
56      /** Speedup for logs */
57      private static final boolean IS_DEBUG = log.isDebugEnabled();
58  
59      /** The used encryption algorithm */
60      private EncryptionType eType;
61  
62      /** Version number of the key under which data is encrypted */
63      private int kvno;
64  
65      /** A flag used to tell if a kvno has been added, as the kvno is optional. */
66      private boolean hasKvno;
67  
68      /** The field containing the enciphered text */
69      private byte[] cipher;
70  
71      /** A constant used when the key is not present */
72      public static final boolean HAS_KVNO = true;
73  
74      // Storage for computed lengths
75      private int eTypeTagLength;
76      private int kvnoTagLength;
77      private int cipherTagLength;
78      private int encryptedDataSeqLength;
79  
80  
81      /**
82       * Creates a new instance of EncryptedData.
83       */
84      public EncryptedData()
85      {
86          hasKvno = !HAS_KVNO;
87      }
88  
89  
90      /**
91       * Creates a new instance of EncryptedData.
92       *
93       * @param eType The encription algorithm
94       * @param kvno The key version
95       * @param cipher the encrypted text
96       */
97      public EncryptedData( EncryptionType eType, int kvno, byte[] cipher )
98      {
99          this.eType = eType;
100         this.hasKvno = kvno > 0;
101         this.kvno = kvno;
102         this.cipher = cipher;
103     }
104 
105 
106     /**
107      * Creates a new instance of EncryptedData.
108      *
109      * @param eType The encription algorithm
110      * @param cipher the encrypted text
111      */
112     public EncryptedData( EncryptionType eType, byte[] cipher )
113     {
114         this.eType = eType;
115         this.hasKvno = !HAS_KVNO;
116         kvno = -1;
117         this.cipher = cipher;
118     }
119 
120 
121     /**
122      * Returns the {@link EncryptionType}.
123      *
124      * @return The {@link EncryptionType}.
125      */
126     public EncryptionType getEType()
127     {
128         return eType;
129     }
130 
131 
132     /**
133      * Set the EncryptionType
134      * @param eType the EncryptionType
135      */
136     public void setEType( EncryptionType eType )
137     {
138         this.eType = eType;
139     }
140 
141 
142     /**
143      * Returns the key version.
144      *
145      * @return The key version.
146      */
147     public int getKvno()
148     {
149         return hasKvno ? kvno : -1;
150     }
151 
152 
153     /**
154      * Set the key version
155      * @param kvno The key version
156      */
157     public void setKvno( int kvno )
158     {
159         this.kvno = kvno;
160         hasKvno = true;
161     }
162 
163 
164     /**
165      * Tells if there is a key version.
166      *
167      * @return <code>true</code> if there is a key version.
168      */
169     public boolean hasKvno()
170     {
171         return hasKvno;
172     }
173 
174 
175     /**
176      * Returns the raw cipher text.
177      *
178      * @return The raw cipher text.
179      */
180     public byte[] getCipher()
181     {
182         return cipher;
183     }
184 
185 
186     /**
187      * Set the cipher text
188      * @param cipher The cipher text
189      */
190     public void setCipher( byte[] cipher )
191     {
192         this.cipher = cipher;
193     }
194 
195 
196     /**
197      * Compute the EncryptedData length
198      * <pre>
199      * EncryptedData :
200      * 
201      * 0x30 L1 EncryptedData sequence
202      *  |
203      *  +--&gt; 0xA1 L2 etype tag
204      *  |     |
205      *  |     +--&gt; 0x02 L2-1 etype (int)
206      *  |
207      *  +--&gt; [0xA2 L3 kvno tag
208      *  |     |
209      *  |     +--&gt; 0x30 L3-1 kvno (int)] (optional)
210      *  |
211      *  +--&gt; 0xA2 L4 cipher tag
212      *        |
213      *        +--&gt; 0x04 L4-1 cipher (OCTET STRING)
214      * </pre>
215      */
216     public int computeLength()
217     {
218         encryptedDataSeqLength = 0;
219 
220         // Compute the encryption Type length
221         int eTypeLength = BerValue.getNbBytes( eType.getValue() );
222         eTypeTagLength = 1 + TLV.getNbBytes( eTypeLength ) + eTypeLength;
223         encryptedDataSeqLength = 1 + TLV.getNbBytes( eTypeTagLength ) + eTypeTagLength;
224 
225         // Compute the kvno length if any
226         if ( hasKvno )
227         {
228             int kvnoLength = BerValue.getNbBytes( kvno );
229             kvnoTagLength = 1 + TLV.getNbBytes( kvnoLength ) + kvnoLength;
230             encryptedDataSeqLength += 1 + TLV.getNbBytes( kvnoTagLength ) + kvnoTagLength;
231         }
232         else
233         {
234             kvnoTagLength = 0;
235         }
236 
237         // Compute the cipher
238         if ( ( cipher == null ) || ( cipher.length == 0 ) )
239         {
240             cipherTagLength = 1 + 1;
241         }
242         else
243         {
244             cipherTagLength = 1 + TLV.getNbBytes( cipher.length ) + cipher.length;
245         }
246 
247         encryptedDataSeqLength += 1 + TLV.getNbBytes( cipherTagLength ) + cipherTagLength;
248 
249         // Compute the whole sequence length
250         return 1 + TLV.getNbBytes( encryptedDataSeqLength ) + encryptedDataSeqLength;
251     }
252 
253 
254     /**
255      * Encode the EncryptedData message to a PDU. 
256      * <pre>
257      * EncryptedData :
258      * 
259      * 0x30 LL
260      *   0xA0 LL 
261      *     0x02 0x01 etype (integer)
262      *   [0xA1 LL 
263      *     0x02 0x01 kvno (integer)] (optional)
264      *   0xA2 LL 
265      *     0x04 LL cipher (OCTET STRING)
266      * </pre>
267      * @param buffer The buffer where to put the PDU. It should have been allocated
268      * before, with the right size.
269      * @return The constructed PDU.
270      */
271     public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
272     {
273         if ( buffer == null )
274         {
275             throw new EncoderException( I18n.err( I18n.ERR_148 ) );
276         }
277 
278         try
279         {
280             // The EncryptedData SEQ Tag
281             buffer.put( UniversalTag.SEQUENCE.getValue() );
282             buffer.put( TLV.getBytes( encryptedDataSeqLength ) );
283 
284             // The etype, first the tag, then the value
285             buffer.put( ( byte ) 0xA0 );
286             buffer.put( TLV.getBytes( eTypeTagLength ) );
287 
288             BerValue.encode( buffer, eType.getValue() );
289 
290             // The kvno, if any, first the tag, then the value
291             if ( hasKvno )
292             {
293                 buffer.put( ( byte ) 0xA1 );
294                 buffer.put( TLV.getBytes( kvnoTagLength ) );
295 
296                 BerValue.encode( buffer, kvno );
297             }
298 
299             // The cipher tag
300             buffer.put( ( byte ) 0xA2 );
301             buffer.put( TLV.getBytes( cipherTagLength ) );
302             BerValue.encode( buffer, cipher );
303         }
304         catch ( BufferOverflowException boe )
305         {
306             log.error( I18n.err( I18n.ERR_141, 1 + TLV.getNbBytes( encryptedDataSeqLength )
307                 + encryptedDataSeqLength, buffer.capacity() ) );
308             throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
309         }
310 
311         if ( IS_DEBUG )
312         {
313             log.debug( "EncryptedData encoding : {}", Strings.dumpBytes( buffer.array() ) );
314             log.debug( "EncryptedData initial value : {}", this );
315         }
316 
317         return buffer;
318     }
319 
320 
321     /**
322      * {@inheritDoc}
323      */
324     @Override
325     public int hashCode()
326     {
327         final int prime = 31;
328         int result = 1;
329         result = prime * result + Arrays.hashCode( cipher );
330         result = prime * result + ( ( eType == null ) ? 0 : eType.hashCode() );
331         result = prime * result + kvno;
332         return result;
333     }
334 
335 
336     /**
337      * {@inheritDoc}
338      */
339     @Override
340     public boolean equals( Object obj )
341     {
342         if ( this == obj )
343         {
344             return true;
345         }
346 
347         if ( !( obj instanceof EncryptedData ) )
348         {
349             return false;
350         }
351 
352         EncryptedData./../../../org/apache/directory/shared/kerberos/components/EncryptedData.html#EncryptedData">EncryptedData other = ( EncryptedData ) obj;
353 
354         if ( !MessageDigest.isEqual( cipher, other.cipher ) )
355         {
356             return false;
357         }
358 
359         if ( eType != other.eType )
360         {
361             return false;
362         }
363 
364         return kvno == other.kvno;
365     }
366 
367 
368     /**
369      * @see Object#toString()
370      */
371     public String toString()
372     {
373         return toString( "" );
374     }
375 
376 
377     /**
378      * @see Object#toString()
379      */
380     public String toString( String tabs )
381     {
382         StringBuilder sb = new StringBuilder();
383 
384         sb.append( tabs ).append( "EncryptedData : {\n" );
385         sb.append( tabs ).append( "    etype: " ).append( eType ).append( '\n' );
386 
387         if ( hasKvno )
388         {
389             sb.append( tabs ).append( "    kvno: " ).append( kvno ).append( '\n' );
390         }
391 
392         sb.append( tabs ).append( "    cipher: " ).append( Strings.dumpBytes( cipher ) ).append( "\n}\n" );
393 
394         return sb.toString();
395     }
396 }