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 16:38:06 +0200 (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 }