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.kerberos.shared.crypto.encryption.EncryptionType;
027 import org.apache.directory.shared.asn1.AbstractAsn1Object;
028 import org.apache.directory.shared.asn1.ber.tlv.TLV;
029 import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
030 import org.apache.directory.shared.asn1.ber.tlv.Value;
031 import org.apache.directory.shared.asn1.codec.EncoderException;
032 import org.apache.directory.shared.ldap.util.StringTools;
033 import org.slf4j.Logger;
034 import org.slf4j.LoggerFactory;
035
036
037 /**
038 * A structure storing an encrypted data element. The ASN.1 grammar is :
039 *
040 * EncryptedData ::= SEQUENCE {
041 * etype [0] Int32 -- EncryptionType --,
042 * kvno [1] UInt32 OPTIONAL,
043 * cipher [2] OCTET STRING -- ciphertext
044 * }
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 * @version $Rev: 642496 $, $Date: 2008-03-29 05:09:22 +0200 (Sat, 29 Mar 2008) $
048 */
049 public class EncryptedData extends AbstractAsn1Object
050 {
051 /** The logger */
052 private static final Logger log = LoggerFactory.getLogger( EncryptedData.class );
053
054 /** Speedup for logs */
055 private static final boolean IS_DEBUG = log.isDebugEnabled();
056
057 /** The used encryption algorithm */
058 private EncryptionType eType;
059
060 /** Version number of the key under which data is encrypted */
061 private int kvno;
062
063 /** A flag used to tell if a kvno has been added, as the kvno is optional. */
064 private boolean hasKvno;
065
066 /** The field containing the enciphered text */
067 private byte[] cipher;
068
069 /** A constant used when the key is not present */
070 public static final boolean HAS_KVNO = true;
071
072 // Storage for computed lengths
073 private transient int eTypeTagLength;
074 private transient int kvnoTagLength;
075 private transient int cipherTagLength;
076 private transient int encryptedDataSeqLength;
077
078
079 /**
080 * Creates a new instance of EncryptedData.
081 */
082 public EncryptedData()
083 {
084 hasKvno = !HAS_KVNO;
085 }
086
087 /**
088 * Creates a new instance of EncryptedData.
089 *
090 * @param eType The encription algorithm
091 * @param kvno The key version
092 * @param cipher the encrypted text
093 */
094 public EncryptedData( EncryptionType eType, int kvno, byte[] cipher )
095 {
096 this.eType = eType;
097 this.hasKvno = kvno > 0;
098 this.kvno = kvno;
099 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 }