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
100
101
102
103
104
105
|
package model.asn1.parsing;
import model.asn1.ASN1Length;
import model.asn1.Tag;
import model.asn1.exceptions.ParseException;
/**
* A mutable model represents a one-way pipe of reading the input DER bytes. It keeps track of the total input and the
* current location, and it provides useful methods of requiring one or more bytes to present in the next.
*/
public class BytesReader {
private final Byte[] rawInput;
private int index;
/**
* EFFECTS: Initialize the reader with the given input and index set to 0.
* REQUIRES: rawInput.length > 0
*/
public BytesReader(Byte[] rawInput) {
this.rawInput = rawInput;
this.index = 0;
}
/**
* EFFECTS: Calculate the number of bytes remaining to read.
*/
public int bytesRemaining() {
return rawInput.length - index;
}
/**
* EFFECTS: Copy the given number of bytes from the [getIndex(), getIndex() + size) and optionally mark as read.
* MODIFIES: this (if markAsRead == true)
* REQUIRES: size <= bytesRemaining(), size > 0
*/
public Byte[] read(int size, boolean markAsRead) {
Byte[] result = new Byte[size];
System.arraycopy(rawInput, index, result, 0, size);
if (markAsRead) {
index += size;
}
return result;
}
/**
* EFFECTS: Copy the given number of bytes from [getIndex(), getIndex() + size) and optionally mark as read.
* Throws {@link ParseException} if size > bytesRemaining().
* MODIFIES: this (if markAsRead == true)
* REQUIRES: size > 0
*/
public Byte[] require(int size, boolean markAsRead) throws ParseException {
validateSize(size);
return read(size, markAsRead);
}
/**
* EFFECTS: Check if size <= bytesRemaining().
* Throws {@link ParseException if not}.
* REQUIRES: size > 0
*/
public void validateSize(int size) throws ParseException {
if (size > bytesRemaining()) {
throw new ParseException(String.format("%d required at location %d, but only has %d before EOF.",
size,
index,
bytesRemaining()));
}
}
/**
* EFFECTS: Check if the next byte has the desired tag, without changing the index.
* Throws {@link ParseException} if the input is illegal (not even a tag or EOF).
*/
public boolean detectTag(Tag desired) throws ParseException {
final int i = index;
final Tag t = new Tag(this);
index = i;
return t.getCls() == desired.getCls()
&& t.isConstructive() == desired.isConstructive()
&& t.getNumber() == desired.getNumber();
}
/**
* EFFECTS: Get the current tag or the tag immediately following (inner) without changing the index.
* Throws {@link ParseException} if the input is illegal (not even a tag or EOF).
*/
public Tag getTag(boolean inner) throws ParseException {
final int i = index;
Tag t = new Tag(this);
if (inner) {
new ASN1Length(this);
t = new Tag(this);
}
index = i;
return t;
}
public Byte[] getRawInput() {
return rawInput;
}
public int getIndex() {
return index;
}
}
|