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 15:38:06 +0100 (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 }