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
96
97
98
99
|
package model.asn1;
import model.asn1.exceptions.ParseException;
import model.asn1.parsing.BytesReader;
import ui.Utils;
/**
* Represents the Length part in DER encoding. It appears after Tag and before Value. It represents the length of the
* encoded Value in bytes.
* For encoding, if the length is <= 127, it is encoded as a single byte, with the highest bit cleared. If it is > 127,
* the initial byte will have its highest bit set, with the remaining 7 bits representing how many of bytes in advance
* are needed to represent the multibyte length value. Then, following are the multibyte length value, encoded in a
* big-endian unsigned integer.
*/
public class ASN1Length implements Encodable {
/**
* The length. It is represented in Java signed 32bit integer, but it should be unsigned.
* Operations should use Integer#unsigned* methods.
*/
private final int length;
/**
* EFFECTS: Initialize the object with the given length.
* REQUIRES: length >= 0
*/
public ASN1Length(int length) {
this.length = length;
}
/**
* EFFECTS: Parse the length from the given DER input.
* Throws {@link ParseException} if the input is invalid:
* - Indefinite length
* - Not enough bytes
* - Initial byte 0b11111111 (See X.690$8.1.3.5)
* - Value too long (compliant to RFC but unsupported by this program): multibyte and # of bytes > 3
* MODIFIES: reader (bytes are read, at least one byte, at most 5 bytes)
*/
public ASN1Length(BytesReader reader) throws ParseException {
final Byte first = reader.require(1, true)[0];
if (first < 0) {
// Multibyte
// 0b11111111
if (first == -1) {
throw new ParseException("The initial byte must not be 0xFF");
}
// Clear the sign bit and get the remaining.
int count = first & 127;
if (count == 0) {
throw new ParseException("Indefinite length is forbidden by DER");
}
final Byte[] values = reader.require(count, true);
// Pad one byte to the top - so it is always unsigned.
Byte[] b1 = new Byte[values.length + 1];
System.arraycopy(values, 0, b1, 1, values.length);
b1[0] = 0x0;
this.length = Utils.bytesToInt(b1);
} else {
this.length = first;
}
}
/**
* EFFECTS: Compute the length to add in the Tag - Length - Value format. For a detailed description on length, see
* class specification.
*/
@Override
public Byte[] encodeDER() {
// Determine the length of the length.
// If the length is <= 127 bytes, use a single byte.
// If the length is > 127 bytes, set the highest bit as 1, and the
// rest of the bits specify how many more bytes the length is, followed
// by a sequence of bytes of the length.
// Setting the length 80 (0b10000000) means indefinite length, which is forbidden
// by DER.
// DER prefers the shortest form.
if (length <= 127) {
// Possible in a single byte.
return new Byte[]{(byte) length};
} else {
// Big-endian encoding of the length. DER uses big-endian.
final Byte[] lengthBytes = Utils.valToByte(length);
final Byte[] result = new Byte[lengthBytes.length + 1];
// Turn-on the highest bit.
result[0] = (byte) (lengthBytes.length | -128);
// Append the length.
System.arraycopy(lengthBytes, 0,
result, 1, lengthBytes.length);
return result;
}
}
/**
* EFFECT: Returns the unsigned integer length.
*/
public int getLength() {
return length;
}
}
|