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; } }