Package org.hxzon.asn1.core.parse

Source Code of org.hxzon.asn1.core.parse.BerInputStream

/*  BerInputStream.java
*
*  Created on May 31, 2006 by William Edward Woody
*/

/*
* Copyright 2007 William Woody, All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Chaos In Motion nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* Contact William Woody at woody@alumni.caltech.edu or at woody@chaosinmotion.com.
* Chaos In Motion is at http://www.chaosinmotion.com
*/

package org.hxzon.asn1.core.parse;

import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.BitSet;

import org.hxzon.asn1.core.type.ext.BitStringPresentation;

/**
* The input stream reader provides primitives for reading some fundamental objects
* from a BER input stream
*/
public class BerInputStream extends LengthInputStream {
    private byte[] _fBuffer; // internal copy buffer

    /**
     * Construct a new input stream reader
     * @param inStream
     */

    public BerInputStream(InputStream inStream) {
        super(inStream);
    }

    /**
     * Internal read routine reads a byte and throws an exception if
     * the end of file has been reached.
     *
     * @return
     * @throws IOException
     * @see java.io.DataInput#readByte()
     */
    private byte readByte() throws IOException {
        int b = read();
        if (b == -1) {
            throw new EOFException();
        }
        _totalLen++;//add by hxzon
        return (byte) b;
    }

    /**
     * Read a BER tag.
     * @return The BER tag read at this location
     * @throws IOException
     */
    public int readBerTag() throws IOException {
        _tagOffset += _totalLen;//hxzon
        _totalLen = 0;//hxzon
        byte h, x;
        int type;

        //change by hxzon
        try {
            h = readByte();
        } catch (EOFException e) {
            //add by hxzon//FIXME
            return Tag.EOFTYPE;
        }
        type = h & 0x1F;
        if (type == 0x1F) {
            /* Multi-byte type */
            type = 0;
            do {
                x = readByte();
                type = (type << 7) | (0x7F & x);
            } while (0 != (0x80 & x));
        }
        //add by hxzon,after read tag
        _lenOffset = _tagOffset + _totalLen;
        //change by hxzon,by Fatih Batuk
//        return Tag.convertTag(0x03 & (h >> 6), type, (0 != (h & 0x20)));
        int hold = Tag.convertTag(0x03 & (h >> 6), type, (0 != (h & 0x20)));
        return (hold & Tag.UNCONSTRUCTED_MASK);
    }

    /**
     * Read a BER length.
     * @return The BER length read at this location. This is -1 if the length is the indefinite form.
     * @throws IOException
     */
    public int readBerLength() throws IOException {
        byte h, x;
        int length;

        h = readByte();
        if (0 == (0x80 & h)) {
            //add by hxzon,after read length
            _valueOffset = _tagOffset + _totalLen;
            _valueLen = h;
            return h; // definite short form
        }
        h &= 0x7F; // definite long form; pull data
        if (h == 0) {
            //add by hxzon,after read length
            _valueOffset = _tagOffset + _totalLen;
            _valueLen = -1;
            return -1; // indefinite long form
        }
        length = 0;
        while (h-- > 0) {
            x = readByte();
            length = (length << 8) | (0x00FF & x);
        }
        //add by hxzon,after read length
        _valueOffset = _tagOffset + _totalLen;
        _valueLen = length;
        return length;
    }

    /**
     * Reads the primitive BER binary object. This assumes ber length is unread
     * @return
     * @throws AsnEncodingException
     * @see com.chaosinmotion.asn1.io.Asn1InputStream#readBoolean()
     */
    public boolean readBoolean() throws IOException {
        byte x;

        int length = readBerLength();
        if (length != 1)
            throw new AsnEncodingException("Unknown boolean value: illegal boolean length");
        x = readByte();
        return (x != 0);
    }

    /**
     * Read a primitive integer object. This assumes the length is unread
     * @return
     * @throws AsnEncodingException
     * @see com.chaosinmotion.asn1.io.Asn1InputStream#readInteger()
     */
    public long readInteger() throws IOException {
        int length = readBerLength();
        return readBerInteger(length);
    }

    /**
     * Internal integer read routine
     * @param length
     * @return
     * @throws IOException
     */
    private long readBerInteger(int length) throws IOException {
        long value = 0;
        byte x;

        if (length == -1)
            throw new AsnEncodingException("Unknown integer value: illegal integer length");
        if (length == 0)
            return value;

        x = readByte();
        value = x; // sign extended

        while (--length > 0) {
            x = readByte();
            value = (value << 8) | (0x00FF & x);
        }

        return value;
    }

    /**
     * Read a primitive BER integer object as an unsigned integer
     * @param length
     * @return
     * @throws IOException
     */
    private long readBerUnsignedInteger(int length) throws IOException {
        long value = 0;
        byte x;

        if (length == -1)
            throw new AsnEncodingException("Unknown integer value: illegal integer length");
        if (length == 0)
            return value;
        while (length-- > 0) {
            x = readByte();
            value = (value << 8) | (0x00FF & x);
        }

        return value;
    }

    /**
     * Reads the real value from the BER string. This assumes the length is unread
     * @return
     * @throws AsnEncodingException
     * @see com.chaosinmotion.asn1.io.Asn1InputStream#readReal()
     */
    public double readReal() throws IOException {
        byte temp;
        byte b;
        int expOctet;
        boolean signNeg;
        int base;
        int f;
        int exponent;
        long mantissa;
        int length;

        length = readBerLength();
        if (length == -1)
            throw new AsnEncodingException("Unknown integer value: illegal integer length");
        if (length == 0)
            return 0;

        b = readByte();
        length--;

        /*
         * From the flags of the input buffer, determine the type of real we're
         * dealing with
         */

        temp = (byte) (0xC0 & b);
        if (0x40 == temp) {
            /* ITU-T Standard X.690, 8.5.8 */
            if (b == 0x40)
                return Double.POSITIVE_INFINITY;
            if (b == 0x41)
                return Double.NEGATIVE_INFINITY;
            throw new AsnEncodingException("Unknown real value: reserved encoding not supported");
        }

        if (0x00 == temp) {
            /* ITU-T Standard X.690, 8.5.7 not supported */
            throw new AsnEncodingException("Unknown real value: ISO 6093 encoding not supported");
        }

        /*
         * Extract the sign, base and f scaling factors (per 8.5.6)
         */

        signNeg = (0 != (temp & 0x40));

        switch (0x03 & (b >> 4)) {
        case 0:
            base = 2;
            break;
        case 1:
            base = 8;
            break;
        case 2:
            base = 16;
            break;
        default:
            throw new AsnEncodingException("Unknown real value: reserved base not supported");
        }

        f = 0x03 & (b >> 2);

        /*
         * Read the octet, encoded as a signed binary integer of length given in the header
         * byte.
         */

        expOctet = (0x03 & b);
        if (expOctet == 3) {
            expOctet = 0x00FF & readByte();
            length--;
        } else {
            expOctet++;
        }
        exponent = (int) readBerInteger(expOctet);
        length -= expOctet;

        /*
         * Read the mantessa and recompute the actual mantessa value
         * based upon the formula in 8.5.6: M = sign * N * 2**F
         */
        mantissa = readBerUnsignedInteger(length);
        if (f != 0)
            mantissa <<= f;
        if (signNeg)
            mantissa = -mantissa;

        /*
         * Now compute the result and return it.
         */
        return mantissa * Math.pow((double) base, (double) exponent);
    }

    /**
     * Read an object identifier. This assumes the length is unread
     * @return
     * @throws AsnEncodingException
     * @see com.chaosinmotion.asn1.io.Asn1InputStream#readOID()
     */
    public long[] readOID() throws IOException {
        long[] value;
        int length;
        int i;
        int v, b;

        length = readBerLength();

        if (length == -1)
            throw new AsnEncodingException("Expected primitive type");
        byte[] bstr = new byte[length];
        read(bstr);

        /*
         * Calculate the OID length
         */
        length = 2;
        for (i = 1; i < bstr.length; ++i) {
            if (bstr[i] >= 0)
                ++length;
        }
        value = new long[length];

        /*
         * Parse the OID
         */
        b = 0x00FF & bstr[0];
        value[0] = b / 40;
        value[1] = b % 40;

        v = 0;
        length = 2;
        for (i = 1; i < bstr.length; ++i) {
            v = v * 128 + 0x007F & bstr[i];
            if (bstr[i] >= 0) {
                value[length++] = v;
                v = 0;
            }
        }

        /*
         * Return the OID as an object
         */
        return value;
    }

    /**
     * Read a relative object identifier. This assumes the length is unread
     * @return
     * @throws IOException
     */
    public long[] readRelativeOID() throws IOException {
        long[] value;
        int length;
        int i;
        int v;

        length = readBerLength();

        if (length == -1)
            throw new AsnEncodingException("Expected primitive type");
        byte[] bstr = new byte[length];
        read(bstr);

        /*
         * Calculate the relative OID length
         */
        length = 0;
        for (i = 0; i < bstr.length; ++i) {
            if (bstr[i] >= 0)
                ++length;
        }
        value = new long[length];

        /*
         * Parse the OID
         */
        v = 0;
        length = 0;
        for (i = 0; i < bstr.length; ++i) {
            v = v * 128 + 0x007F & bstr[i];
            if (bstr[i] >= 0) {
                value[length++] = v;
                v = 0;
            }
        }

        /*
         * Return the OID as an object
         */
        return value;
    }

    /**
     * Internal routine for reading bit strings. This assumes he length is
     * unread
     *
     * @param set The set of bits that is being created
     * @param start The first bit where to flip stuff
     * @return One more than the highest bit read
     */
    private int readBerBitString(BitSet set, int start, boolean primitive) throws IOException {
        int length;
        byte b;
        byte m;
        int i, j;

        if (primitive) {
            /*
             * The current tag is primitive, which means what follows is the
             * array of bits
             */

            length = readBerLength();
            if (length < 0)
                throw new AsnEncodingException("Ill formed bit stream");
            b = readByte();
            --length;
            for (i = 1; i < length; ++i) {
                m = readByte();
                for (j = 0; j < 8; ++j) {
                    if (0 != (m & (0x80 >> j)))
                        set.set(start);
                    ++start;
                }
            }
            m = readByte();
            for (j = 0; j < 8 - b; ++j) {
                if (0 != (m & (0x80 >> j)))
                    set.set(start);
                ++start;
            }
        } else {
            /*
             * The current tag is compound. Read all of the components
             */

            ReadSequence rseq = new ReadSequence("read bit string", this);

            int tag;
            while (0 != (tag = rseq.readBerTag())) {
                start = readBerBitString(set, start, 0 == (tag & Tag.CONSTRUCTED));
            }
        }

        return start;
    }

    private int readBerBitString2(BitStringPresentation set, int start, boolean primitive) throws IOException {
        int length;
        byte bitPadLen;
        byte bitByte;
        int i, j;

        if (primitive) {
            /*
             * The current tag is primitive, which means what follows is the
             * array of bits
             */

            length = readBerLength();
            if (length < 0) {
                throw new AsnEncodingException("Ill formed bit stream");
            }
            //add by hxzon for when len=1//FIXME is it right when len=1 means no pad?
            if (length != 1) {
                bitPadLen = readByte();
                --length;
            } else {
                bitPadLen = 0;
            }
            //add by hxzon
            set.init(bitPadLen, length);
            //end add
            for (i = 1; i < length; ++i) {
                bitByte = readByte();
                for (j = 0; j < 8; ++j) {
                    if (0 != (bitByte & (0x80 >> j)))
                        set.getValue().set(start);
                    ++start;
                }
            }
            bitByte = readByte();
            for (j = 0; j < 8 - bitPadLen; ++j) {
                if (0 != (bitByte & (0x80 >> j)))
                    set.getValue().set(start);
                ++start;
            }
        } else {
            /*
             * The current tag is compound. Read all of the components
             */

            ReadSequence rseq = new ReadSequence("read bit string2", this);

            int tag;
            while (0 != (tag = rseq.readBerTag())) {
                start = readBerBitString2(set, start, 0 == (tag & Tag.CONSTRUCTED));
            }
        }

        return start;
    }

    /**
     * Routine to read a bit string. The flag is the primitive setting from the
     * loaded tag
     * @param primitive
     * @return
     * @throws IOException
     */
    public BitSet readBitString(boolean primitive) throws IOException {
        BitSet set = new BitSet();
        readBerBitString(set, 0, primitive);
        return set;
    }

    public BitStringPresentation readBitString2(boolean primitive) throws IOException {
        BitStringPresentation set = new BitStringPresentation();
        readBerBitString2(set, 0, primitive);
        return set;
    }

    /**
     * Internal routine copies the specified number of bytes to the output stream.
     * @param stream
     * @param length
     * @throws IOException
     */
    private void copyToStream(OutputStream stream, int length) throws IOException {
        int off, rlen;

        if (_fBuffer == null)
            _fBuffer = new byte[1024]; // small buffer for copy purposes

        off = 0;
        while (off < length) {
            rlen = length - off;
            if (rlen > _fBuffer.length)
                rlen = _fBuffer.length;

            rlen = read(_fBuffer, 0, rlen);
            if (rlen == -1)
                throw new AsnEncodingException("EOF reached unexpectedly");
            stream.write(_fBuffer, 0, rlen);

            off += rlen;
        }
    }

    /**
     * Read a primitive octet string. The flag is the primitive setting from the
     * loaded tag
     * @param primitive
     * @param stream
     * @throws AsnEncodingException
     * @see com.chaosinmotion.asn1.io.Asn1InputStream#readOctetString(java.io.OutputStream)
     */
    public void readOctetString(OutputStream stream, boolean primitive) throws IOException {
        int length;

        /*
         * From the current tag determine if this is a primitive or
         * a compound string.
         */
        if (primitive) {
            /*
             * This is a primitive string. Simply copy the octets
             */
            length = readBerLength();
            copyToStream(stream, length);
        } else {
            /*
             * This is a compound string. Read each of the components
             * as strings inside of me, recursing for each
             */
            ReadSequence rseq = new ReadSequence("read octet string", this);
            int tag;

            while (0 != (tag = rseq.readBerTag())) {
                readOctetString(stream, 0 == (tag & Tag.CONSTRUCTED));
            }
        }
    }

    /**
     * Read a primitive octet string. The flag is the primitive setting from the
     * loaded tag
     * @param primitive
     * @return
     */
    public byte[] readOctetString(boolean primitive) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        readOctetString(baos, primitive);
        baos.close();
        byte[] result = baos.toByteArray();
        //add by hxzon
        if (primitive) {
            _totalLen += result.length;
//      logger.trace("read octet string:total len:"+totalLen);
        }
        //end add
        return result;
    }

    //-----------------------------------------
    //add by hxzon for offset and len
    private int _tagOffset = 0;
    private int _lenOffset = 0;
    private int _valueOffset = 0;
    private int _valueLen = 0;
    private int _totalLen = 0;

    public int getTagOffset() {
        return _tagOffset;
    }

    public void setTagOffset(int offset) {
        this._tagOffset = offset;
    }

    public int getTotalLen() {
        return _totalLen;
    }

    public void setTotalLen(int len) {
        this._totalLen = len;
    }

    public int getLenOffset() {
        return _lenOffset;
    }

    public void setLenOffset(int lenOffset) {
        this._lenOffset = lenOffset;
    }

    public int getValueOffset() {
        return _valueOffset;
    }

    public void setValueOffset(int valueOffset) {
        this._valueOffset = valueOffset;
    }

    public int getValueLen() {
        return _valueLen;
    }

    public void setValueLen(int valueLen) {
        this._valueLen = valueLen;
    }
}
TOP

Related Classes of org.hxzon.asn1.core.parse.BerInputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.