1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
package model.asn1;
import model.asn1.exceptions.ParseException;
import model.asn1.parsing.BytesReader;
import ui.Utils;
import java.math.BigInteger;
/**
* Represents the ASN.1 BIT STRING (0x3) type. Bit strings represent bytes by padding to the LSB (like a bitstream).
* The bits are encoded as multiple bytes, but the unused bits are stored on the <b>lowest</b> part.
* For example, consider:
* <pre>
* 0b 00000001 10111001 01110111 (pad to the highest)
* Will be encoded into
* 0b 01101110 01011101 11000000 (pad to the lowest)
* ^^^^^^
* </pre>
* Before the encoded value, there will be another byte denoting how many padding bits are added to the right.
* That is, the final encoding is:
* <pre>
* 0b 00000110 01101110 01011101 11000000
* ^ 6 ^ ^ Original Number ^^Pad^
* </pre>
* <p>
* BIT STRING has nothing to do with encoding bytes as printable strings (base10 or base16 or ASCII).
*/
public class BitString extends ASN1Object {
/**
* The X.680 universal class tag assignment.
*/
public static final Tag TAG = new Tag(TagClass.UNIVERSAL, false, 0x3);
private final int unused;
private final Byte[] val;
/**
* EFFECT: Init with tags, unused, and val. For tags, see {@link ASN1Object}.
* REQUIRES: 0 <= unused < 8, the last byte in val must have the lowest $unused bits zero.
*/
public BitString(Tag tag, Tag parentTag,
final int unused,
final Byte[] val) {
super(tag, parentTag);
this.unused = unused;
this.val = val;
}
/**
* EFFECT: Parse the input DER.
* Throws {@link ParseException} if the input is invalid:
* - Unused is not in 0 <= unused < 8
* - The last byte does not have its lowest $unused bits zero
* - Other issues found according to {@link ASN1Object}
*/
public BitString(BytesReader encoded, boolean hasParentTag) throws ParseException {
super(encoded, hasParentTag);
this.unused = encoded.require(1, true)[0];
if (unused < 0 || unused > 7) {
throw new ParseException("Illegal unused byte: " + unused);
}
this.val = encoded.require(getLength() - 1, true);
if ((byte) (val[val.length - 1] << (8 - unused)) != 0) {
throw new ParseException(String.format("The last byte: 0x%02X does not have %d zero bits.",
val[val.length - 1],
unused));
}
}
/**
* EFFECTS: Get the converted form that has padding on MSB. The leftmost zero byte, if any, is removed.
*/
public Byte[] getConvertedVal() {
return Utils.byteToByte(new BigInteger(Utils.byteToByte(val)).shiftRight(unused).toByteArray());
}
/**
* EFFECTS: Encode into DER.
*/
@Override
public Byte[] encodeValueDER() {
Byte[] arr = new Byte[val.length + 1];
arr[0] = (byte) unused;
System.arraycopy(val, 0, arr, 1, val.length);
return arr;
}
public int getUnused() {
return unused;
}
public Byte[] getVal() {
return val;
}
}
|