diff options
Diffstat (limited to 'core/src/main/java/org/spongycastle/asn1/DERBitString.java')
-rw-r--r-- | core/src/main/java/org/spongycastle/asn1/DERBitString.java | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/asn1/DERBitString.java b/core/src/main/java/org/spongycastle/asn1/DERBitString.java new file mode 100644 index 00000000..9a2d2d03 --- /dev/null +++ b/core/src/main/java/org/spongycastle/asn1/DERBitString.java @@ -0,0 +1,318 @@ +package org.spongycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.io.Streams; + +public class DERBitString + extends ASN1Primitive + implements ASN1String +{ + private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + protected byte[] data; + protected int padBits; + + /** + * @param bitString an int containing the BIT STRING + * @return the correct number of pad bits for a bit string defined in + * a 32 bit constant + */ + static protected int getPadBits( + int bitString) + { + int val = 0; + for (int i = 3; i >= 0; i--) + { + // + // this may look a little odd, but if it isn't done like this pre jdk1.2 + // JVM's break! + // + if (i != 0) + { + if ((bitString >> (i * 8)) != 0) + { + val = (bitString >> (i * 8)) & 0xFF; + break; + } + } + else + { + if (bitString != 0) + { + val = bitString & 0xFF; + break; + } + } + } + + if (val == 0) + { + return 7; + } + + + int bits = 1; + + while (((val <<= 1) & 0xFF) != 0) + { + bits++; + } + + return 8 - bits; + } + + /** + * @param bitString an int containing the BIT STRING + * @return the correct number of bytes for a bit string defined in + * a 32 bit constant + */ + static protected byte[] getBytes(int bitString) + { + int bytes = 4; + for (int i = 3; i >= 1; i--) + { + if ((bitString & (0xFF << (i * 8))) != 0) + { + break; + } + bytes--; + } + + byte[] result = new byte[bytes]; + for (int i = 0; i < bytes; i++) + { + result[i] = (byte) ((bitString >> (i * 8)) & 0xFF); + } + + return result; + } + + /** + * return a Bit String from the passed in object + * + * @param obj a DERBitString or an object that can be converted into one. + * @exception IllegalArgumentException if the object cannot be converted. + * @return a DERBitString instance, or null. + */ + public static DERBitString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBitString) + { + return (DERBitString)obj; + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Bit String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + * @return a DERBitString instance, or null. + */ + public static DERBitString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERBitString) + { + return getInstance(o); + } + else + { + return fromOctetString(((ASN1OctetString)o).getOctets()); + } + } + + protected DERBitString( + byte data, + int padBits) + { + this.data = new byte[1]; + this.data[0] = data; + this.padBits = padBits; + } + + /** + * @param data the octets making up the bit string. + * @param padBits the number of extra bits at the end of the string. + */ + public DERBitString( + byte[] data, + int padBits) + { + this.data = data; + this.padBits = padBits; + } + + public DERBitString( + byte[] data) + { + this(data, 0); + } + + public DERBitString( + int value) + { + this.data = getBytes(value); + this.padBits = getPadBits(value); + } + + public DERBitString( + ASN1Encodable obj) + throws IOException + { + this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); + this.padBits = 0; + } + + public byte[] getBytes() + { + return data; + } + + public int getPadBits() + { + return padBits; + } + + + /** + * @return the value of the bit string as an int (truncating if necessary) + */ + public int intValue() + { + int value = 0; + + for (int i = 0; i != data.length && i != 4; i++) + { + value |= (data[i] & 0xff) << (8 * i); + } + + return value; + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + byte[] bytes = new byte[getBytes().length + 1]; + + bytes[0] = (byte)getPadBits(); + System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1); + + out.writeEncoded(BERTags.BIT_STRING, bytes); + } + + public int hashCode() + { + return padBits ^ Arrays.hashCode(data); + } + + protected boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERBitString)) + { + return false; + } + + DERBitString other = (DERBitString)o; + + return this.padBits == other.padBits + && Arrays.areEqual(this.data, other.data); + } + + public String getString() + { + StringBuffer buf = new StringBuffer("#"); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(this); + } + catch (IOException e) + { + throw new RuntimeException("internal error encoding BitString"); + } + + byte[] string = bOut.toByteArray(); + + for (int i = 0; i != string.length; i++) + { + buf.append(table[(string[i] >>> 4) & 0xf]); + buf.append(table[string[i] & 0xf]); + } + + return buf.toString(); + } + + public String toString() + { + return getString(); + } + + static DERBitString fromOctetString(byte[] bytes) + { + if (bytes.length < 1) + { + throw new IllegalArgumentException("truncated BIT STRING detected"); + } + + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + + if (data.length != 0) + { + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + } + + return new DERBitString(data, padBits); + } + + static DERBitString fromInputStream(int length, InputStream stream) + throws IOException + { + if (length < 1) + { + throw new IllegalArgumentException("truncated BIT STRING detected"); + } + + int padBits = stream.read(); + byte[] data = new byte[length - 1]; + + if (data.length != 0) + { + if (Streams.readFully(stream, data) != data.length) + { + throw new EOFException("EOF encountered in middle of BIT STRING"); + } + } + + return new DERBitString(data, padBits); + } +} |