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