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/ASN1Length.java | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/main/model/asn1/ASN1Length.java (limited to 'src/main/model/asn1/ASN1Length.java') diff --git a/src/main/model/asn1/ASN1Length.java b/src/main/model/asn1/ASN1Length.java new file mode 100644 index 0000000..e85689c --- /dev/null +++ b/src/main/model/asn1/ASN1Length.java @@ -0,0 +1,101 @@ +package model.asn1; + +import model.asn1.exceptions.ParseException; +import model.asn1.parsing.BytesReader; +import ui.Utils; + +import java.util.Arrays; + +/** + * 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; + } +} -- cgit v1.2.3