Home | History | Annotate | Download | only in asn1
      1 package org.bouncycastle.asn1;
      2 
      3 import java.io.ByteArrayOutputStream;
      4 import java.io.EOFException;
      5 import java.io.IOException;
      6 import java.io.InputStream;
      7 
      8 import org.bouncycastle.util.Arrays;
      9 import org.bouncycastle.util.io.Streams;
     10 
     11 /**
     12  * Base class for BIT STRING objects
     13  */
     14 public abstract class ASN1BitString
     15     extends ASN1Primitive
     16     implements ASN1String
     17 {
     18     private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
     19 
     20     protected final byte[]      data;
     21     protected final int         padBits;
     22 
     23     /**
     24      * @param bitString an int containing the BIT STRING
     25      * @return the correct number of pad bits for a bit string defined in
     26      * a 32 bit constant
     27      */
     28     static protected int getPadBits(
     29         int bitString)
     30     {
     31         int val = 0;
     32         for (int i = 3; i >= 0; i--)
     33         {
     34             //
     35             // this may look a little odd, but if it isn't done like this pre jdk1.2
     36             // JVM's break!
     37             //
     38             if (i != 0)
     39             {
     40                 if ((bitString >> (i * 8)) != 0)
     41                 {
     42                     val = (bitString >> (i * 8)) & 0xFF;
     43                     break;
     44                 }
     45             }
     46             else
     47             {
     48                 if (bitString != 0)
     49                 {
     50                     val = bitString & 0xFF;
     51                     break;
     52                 }
     53             }
     54         }
     55 
     56         if (val == 0)
     57         {
     58             return 0;
     59         }
     60 
     61         int bits = 1;
     62 
     63         while (((val <<= 1) & 0xFF) != 0)
     64         {
     65             bits++;
     66         }
     67 
     68         return 8 - bits;
     69     }
     70 
     71     /**
     72      * @param bitString an int containing the BIT STRING
     73      * @return the correct number of bytes for a bit string defined in
     74      * a 32 bit constant
     75      */
     76     static protected byte[] getBytes(int bitString)
     77     {
     78         if (bitString == 0)
     79         {
     80             return new byte[0];
     81         }
     82 
     83         int bytes = 4;
     84         for (int i = 3; i >= 1; i--)
     85         {
     86             if ((bitString & (0xFF << (i * 8))) != 0)
     87             {
     88                 break;
     89             }
     90             bytes--;
     91         }
     92 
     93         byte[] result = new byte[bytes];
     94         for (int i = 0; i < bytes; i++)
     95         {
     96             result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
     97         }
     98 
     99         return result;
    100     }
    101 
    102     /**
    103      * Base constructor.
    104      *
    105      * @param data the octets making up the bit string.
    106      * @param padBits the number of extra bits at the end of the string.
    107      */
    108     public ASN1BitString(
    109         byte[]  data,
    110         int     padBits)
    111     {
    112         if (data == null)
    113         {
    114             throw new NullPointerException("data cannot be null");
    115         }
    116         if (data.length == 0 && padBits != 0)
    117         {
    118             throw new IllegalArgumentException("zero length data with non-zero pad bits");
    119         }
    120         if (padBits > 7 || padBits < 0)
    121         {
    122             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
    123         }
    124 
    125         this.data = Arrays.clone(data);
    126         this.padBits = padBits;
    127     }
    128 
    129     /**
    130      * Return a String representation of this BIT STRING
    131      *
    132      * @return a String representation.
    133      */
    134     public String getString()
    135     {
    136         StringBuffer          buf = new StringBuffer("#");
    137         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    138         ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
    139 
    140         try
    141         {
    142             aOut.writeObject(this);
    143         }
    144         catch (IOException e)
    145         {
    146             throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e);
    147         }
    148 
    149         byte[]    string = bOut.toByteArray();
    150 
    151         for (int i = 0; i != string.length; i++)
    152         {
    153             buf.append(table[(string[i] >>> 4) & 0xf]);
    154             buf.append(table[string[i] & 0xf]);
    155         }
    156 
    157         return buf.toString();
    158     }
    159 
    160     /**
    161      * @return the value of the bit string as an int (truncating if necessary)
    162      */
    163     public int intValue()
    164     {
    165         int value = 0;
    166         byte[] string = data;
    167 
    168         if (padBits > 0 && data.length <= 4)
    169         {
    170             string = derForm(data, padBits);
    171         }
    172 
    173         for (int i = 0; i != string.length && i != 4; i++)
    174         {
    175             value |= (string[i] & 0xff) << (8 * i);
    176         }
    177 
    178         return value;
    179     }
    180 
    181     /**
    182      * Return the octets contained in this BIT STRING, checking that this BIT STRING really
    183      * does represent an octet aligned string. Only use this method when the standard you are
    184      * following dictates that the BIT STRING will be octet aligned.
    185      *
    186      * @return a copy of the octet aligned data.
    187      */
    188     public byte[] getOctets()
    189     {
    190         if (padBits != 0)
    191         {
    192             throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
    193         }
    194 
    195         return Arrays.clone(data);
    196     }
    197 
    198     public byte[] getBytes()
    199     {
    200         return derForm(data, padBits);
    201     }
    202 
    203     public int getPadBits()
    204     {
    205         return padBits;
    206     }
    207 
    208     public String toString()
    209     {
    210         return getString();
    211     }
    212 
    213     public int hashCode()
    214     {
    215         return padBits ^ Arrays.hashCode(this.getBytes());
    216     }
    217 
    218     protected boolean asn1Equals(
    219         ASN1Primitive  o)
    220     {
    221         if (!(o instanceof ASN1BitString))
    222         {
    223             return false;
    224         }
    225 
    226         ASN1BitString other = (ASN1BitString)o;
    227 
    228         return this.padBits == other.padBits
    229             && Arrays.areEqual(this.getBytes(), other.getBytes());
    230     }
    231 
    232     protected static byte[] derForm(byte[] data, int padBits)
    233     {
    234         byte[] rv = Arrays.clone(data);
    235         // DER requires pad bits be zero
    236         if (padBits > 0)
    237         {
    238             rv[data.length - 1] &= 0xff << padBits;
    239         }
    240 
    241         return rv;
    242     }
    243 
    244     static ASN1BitString fromInputStream(int length, InputStream stream)
    245         throws IOException
    246     {
    247         if (length < 1)
    248         {
    249             throw new IllegalArgumentException("truncated BIT STRING detected");
    250         }
    251 
    252         int padBits = stream.read();
    253         byte[] data = new byte[length - 1];
    254 
    255         if (data.length != 0)
    256         {
    257             if (Streams.readFully(stream, data) != data.length)
    258             {
    259                 throw new EOFException("EOF encountered in middle of BIT STRING");
    260             }
    261 
    262             if (padBits > 0 && padBits < 8)
    263             {
    264                 if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xff << padBits)))
    265                 {
    266                     return new DLBitString(data, padBits);
    267                 }
    268             }
    269         }
    270 
    271         return new DERBitString(data, padBits);
    272     }
    273 
    274     public ASN1Primitive getLoadedObject()
    275     {
    276         return this.toASN1Primitive();
    277     }
    278 
    279     ASN1Primitive toDERObject()
    280     {
    281         return new DERBitString(data, padBits);
    282     }
    283 
    284     ASN1Primitive toDLObject()
    285     {
    286         return new DLBitString(data, padBits);
    287     }
    288 
    289     abstract void encode(ASN1OutputStream out)
    290         throws IOException;
    291 }
    292