aboutsummaryrefslogtreecommitdiff
path: root/src/main/model/asn1/BitString.java
blob: 0561f24f99fd43dd6b996d082a7f2a98deb291d5 (plain)
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>
 *
 * 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;
    }
}