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.KerberosConstants; 036import org.apache.directory.shared.kerberos.crypto.checksum.ChecksumType; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040 041/** 042 * The Checksum structure is used to store a checksum associated to a type. 043 * 044 * The ASN.1 grammar is : 045 * <pre> 046 * Checksum ::= SEQUENCE { 047 * cksumtype [0] Int32, 048 * checksum [1] OCTET STRING 049 * } 050 * </pre> 051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 052 */ 053public class Checksum implements Asn1Object 054{ 055 /** The logger */ 056 private static final Logger log = LoggerFactory.getLogger( Checksum.class ); 057 058 /** Speedup for logs */ 059 private static final boolean IS_DEBUG = log.isDebugEnabled(); 060 061 /** The checksum type used */ 062 private ChecksumType cksumtype; 063 064 /** The byte array containing the checksum */ 065 private byte[] checksum; 066 067 // Storage for computed lengths 068 private int checksumTypeLength; 069 private int checksumBytesLength; 070 private int checksumLength; 071 072 073 /** 074 * Creates a new instance of Checksum. 075 */ 076 public Checksum() 077 { 078 } 079 080 081 /** 082 * Creates a new instance of Checksum. 083 * 084 * @param cksumtype The checksum type used 085 * @param checksum The checksum value 086 */ 087 public Checksum( ChecksumType cksumtype, byte[] checksum ) 088 { 089 this.cksumtype = cksumtype; 090 this.checksum = checksum; 091 } 092 093 094 /** 095 * {@inheritDoc} 096 */ 097 @Override 098 public int hashCode() 099 { 100 int hash = 37; 101 hash = hash * 17 + cksumtype.hashCode(); 102 hash = hash * 17 + Arrays.hashCode( checksum ); 103 104 return hash; 105 } 106 107 108 /** 109 * @see Object#equals(Object) 110 */ 111 @Override 112 public boolean equals( Object o ) 113 { 114 if ( this == o ) 115 { 116 return true; 117 } 118 119 if ( !( o instanceof Checksum ) ) 120 { 121 return false; 122 } 123 124 Checksum that = ( Checksum ) o; 125 126 return ( cksumtype == that.cksumtype ) && ( MessageDigest.isEqual( checksum, that.checksum ) ); 127 } 128 129 130 /** 131 * Returns the checksum value. 132 * 133 * @return The checksum value. 134 */ 135 public byte[] getChecksumValue() 136 { 137 return checksum; 138 } 139 140 141 /** 142 * Set the checksum Value. 143 * 144 * @param checksum The checksum value 145 */ 146 public void setChecksumValue( byte[] checksum ) 147 { 148 this.checksum = checksum; 149 } 150 151 152 /** 153 * Returns the {@link ChecksumType}. 154 * 155 * @return The {@link ChecksumType}. 156 */ 157 public ChecksumType getChecksumType() 158 { 159 return cksumtype; 160 } 161 162 163 /** 164 * Set the {@link ChecksumType}. 165 * 166 * @param cksumType The checksum algorithm used 167 */ 168 public void setChecksumType( ChecksumType cksumType ) 169 { 170 this.cksumtype = cksumType; 171 } 172 173 174 /** 175 * Compute the checksum length 176 * <pre> 177 * Checksum : 178 * 179 * 0x30 L1 checksum sequence 180 * | 181 * +--> 0xA0 L2 cksumtype tag 182 * | | 183 * | +--> 0x02 L2-1 cksumtype (int) 184 * | 185 * +--> 0xA1 L3 checksum tag 186 * | 187 * +--> 0x04 L3-1 checksum (OCTET STRING) 188 * 189 * where L1 = L2 + lenght(0xA0) + length(L2) + 190 * L3 + lenght(0xA1) + length(L3) 191 * and 192 * L2 = L2-1 + length(0x02) + length( L2-1) 193 * L3 = L3-1 + length(0x04) + length( L3-1) 194 * </pre> 195 */ 196 public int computeLength() 197 { 198 // Compute the checksulType. The Length will always be contained in 1 byte 199 checksumTypeLength = 1 + 1 + BerValue.getNbBytes( cksumtype.getValue() ); 200 checksumLength = 1 + TLV.getNbBytes( checksumTypeLength ) + checksumTypeLength; 201 202 // Compute the checksum Value 203 if ( checksum == null ) 204 { 205 checksumBytesLength = 1 + 1; 206 } 207 else 208 { 209 checksumBytesLength = 1 + TLV.getNbBytes( checksum.length ) + checksum.length; 210 } 211 212 checksumLength += 1 + TLV.getNbBytes( checksumBytesLength ) + checksumBytesLength; 213 214 // Compute the whole sequence length 215 return 1 + TLV.getNbBytes( checksumLength ) + checksumLength; 216 } 217 218 219 /** 220 * Encode the Checksum message to a PDU. 221 * 222 * <pre> 223 * Checksum : 224 * 225 * 0x30 LL 226 * 0xA0 LL 227 * 0x02 0x01 cksumtype 228 * 0xA1 LL 229 * 0x04 LL Checksum 230 * </pre> 231 * @param buffer The buffer where to put the PDU. It should have been allocated 232 * before, with the right size. 233 * @return The constructed PDU. 234 */ 235 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 236 { 237 if ( buffer == null ) 238 { 239 throw new EncoderException( I18n.err( I18n.ERR_148 ) ); 240 } 241 242 try 243 { 244 // The Checksum SEQ Tag 245 buffer.put( UniversalTag.SEQUENCE.getValue() ); 246 buffer.put( TLV.getBytes( checksumLength ) ); 247 248 // The cksumtype, first the tag, then the value 249 buffer.put( ( byte ) KerberosConstants.CHECKSUM_TYPE_TAG ); 250 buffer.put( TLV.getBytes( checksumTypeLength ) ); 251 BerValue.encode( buffer, cksumtype.getValue() ); 252 253 // The checksum, first the tag, then the value 254 buffer.put( ( byte ) KerberosConstants.CHECKSUM_CHECKSUM_TAG ); 255 buffer.put( TLV.getBytes( checksumBytesLength ) ); 256 BerValue.encode( buffer, checksum ); 257 } 258 catch ( BufferOverflowException boe ) 259 { 260 log.error( I18n.err( I18n.ERR_140, 1 + TLV.getNbBytes( checksumLength ) + checksumLength, 261 buffer.capacity() ) ); 262 throw new EncoderException( I18n.err( I18n.ERR_138 ), boe ); 263 } 264 265 if ( IS_DEBUG ) 266 { 267 log.debug( "Checksum encoding : {}", Strings.dumpBytes( buffer.array() ) ); 268 log.debug( "Checksum initial value : {}", this ); 269 } 270 271 return buffer; 272 } 273 274 275 /** 276 * @see Object#toString() 277 */ 278 public String toString() 279 { 280 return toString( "" ); 281 } 282 283 284 /** 285 * @see Object#toString() 286 */ 287 public String toString( String tabs ) 288 { 289 StringBuilder sb = new StringBuilder(); 290 291 sb.append( tabs ).append( "Checksum : {\n" ); 292 sb.append( tabs ).append( " cksumtype: " ).append( cksumtype ).append( '\n' ); 293 294 if ( checksum != null ) 295 { 296 sb.append( tabs + " checksum:" ).append( Strings.dumpBytes( checksum ) ).append( '\n' ); 297 } 298 299 sb.append( tabs + "}\n" ); 300 301 return sb.toString(); 302 } 303}