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