From d342a45d98c4795b3a3fe1aaef5236ad4a782b55 Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Thu, 12 Oct 2023 12:10:33 +0800 Subject: Implement data structures from X.680, X.501, X.509, and PKCS#10, with X.690 encoding / decoding support The implementation took four days, and it is still a little bit rough. Updated version should arrive soon. Signed-off-by: Yuuta Liang --- src/main/model/asn1/parsing/BytesReader.java | 105 +++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/main/model/asn1/parsing/BytesReader.java (limited to 'src/main/model/asn1/parsing/BytesReader.java') diff --git a/src/main/model/asn1/parsing/BytesReader.java b/src/main/model/asn1/parsing/BytesReader.java new file mode 100644 index 0000000..3e11ea6 --- /dev/null +++ b/src/main/model/asn1/parsing/BytesReader.java @@ -0,0 +1,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; + } +} -- cgit v1.2.3