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 */ 020package org.apache.directory.shared.kerberos.components; 021 022 023import java.nio.BufferOverflowException; 024import java.nio.ByteBuffer; 025import java.security.MessageDigest; 026import java.util.Arrays; 027 028import org.apache.directory.api.asn1.Asn1Object; 029import org.apache.directory.api.asn1.EncoderException; 030import org.apache.directory.api.asn1.ber.tlv.BerValue; 031import org.apache.directory.api.asn1.ber.tlv.TLV; 032import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 033import org.apache.directory.api.util.Strings; 034import org.apache.directory.server.i18n.I18n; 035import org.apache.directory.shared.kerberos.codec.types.EncryptionType; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039 040/** 041 * A structure storing an encrypted data element. The ASN.1 grammar is : 042 * <pre> 043 * EncryptedData ::= SEQUENCE { 044 * etype [0] Int32 -- EncryptionType --, 045 * kvno [1] UInt32 OPTIONAL, 046 * cipher [2] OCTET STRING -- ciphertext 047 * } 048 *</pre> 049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 050 */ 051public class EncryptedData implements Asn1Object 052{ 053 /** The logger */ 054 private static final Logger log = LoggerFactory.getLogger( EncryptedData.class ); 055 056 /** Speedup for logs */ 057 private static final boolean IS_DEBUG = log.isDebugEnabled(); 058 059 /** The used encryption algorithm */ 060 private EncryptionType eType; 061 062 /** Version number of the key under which data is encrypted */ 063 private int kvno; 064 065 /** A flag used to tell if a kvno has been added, as the kvno is optional. */ 066 private boolean hasKvno; 067 068 /** The field containing the enciphered text */ 069 private byte[] cipher; 070 071 /** A constant used when the key is not present */ 072 public static final boolean HAS_KVNO = true; 073 074 // Storage for computed lengths 075 private int eTypeTagLength; 076 private int kvnoTagLength; 077 private int cipherTagLength; 078 private int encryptedDataSeqLength; 079 080 081 /** 082 * Creates a new instance of EncryptedData. 083 */ 084 public EncryptedData() 085 { 086 hasKvno = !HAS_KVNO; 087 } 088 089 090 /** 091 * Creates a new instance of EncryptedData. 092 * 093 * @param eType The encription algorithm 094 * @param kvno The key version 095 * @param cipher the encrypted text 096 */ 097 public EncryptedData( EncryptionType eType, int kvno, byte[] cipher ) 098 { 099 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 * +--> 0xA1 L2 etype tag 204 * | | 205 * | +--> 0x02 L2-1 etype (int) 206 * | 207 * +--> [0xA2 L3 kvno tag 208 * | | 209 * | +--> 0x30 L3-1 kvno (int)] (optional) 210 * | 211 * +--> 0xA2 L4 cipher tag 212 * | 213 * +--> 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 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}