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