package model.asn1; import model.asn1.exceptions.ParseException; import model.asn1.parsing.BytesReader; import model.pki.cert.TbsCertificate; import ui.Utils; import java.math.BigInteger; import java.util.Arrays; /** * An ASN.1 INTEGER type. By spec, it can be arbitrarily long. But just like it's impossible to have an * endless tape in a turing machine, this implementation uses fixed length internally to represent ints. */ public class Int extends ASN1Object { /** * The X.680 universal class tag assignment. */ public static final Tag TAG = new Tag(TagClass.UNIVERSAL, false, 0x2); private final BigInteger value; /** * EFFECTS: Initiate the INTEGER object with the given tag and an optional context-specific tag number for explicit * encoding. For more information, consult {@link ASN1Object}. * REQUIRES: Consult {@link ASN1Object}. */ public Int(Tag tag, Tag parentTag, BigInteger value) { super(tag, parentTag); this.value = value; } /** * EFFECTS: Initiate the INTEGER object with the given tag and an optional context-specific tag number for explicit * encoding. For more information, consult {@link ASN1Object}. * REQUIRES: Consult {@link ASN1Object}. */ public Int(Tag tag, Tag parentTag, long value) { this(tag, parentTag, BigInteger.valueOf(value)); } /** * EFFECTS: Parse input and get the int value. Tags are parsed in {@link ASN1Object}. * Throws {@link ParseException} if encoded value are invalid: * - Early EOF (not enough bytes) * - Zero bytes length * - Other issues denoted in {@link ASN1Object} * MODIFIES: this, encoded */ public Int(BytesReader encoded, boolean hasParentTag) throws ParseException { super(encoded, hasParentTag); if (getLength() == 0) { throw new ParseException("Integer with zero length"); } this.value = new BigInteger(Utils.byteToByte(encoded.require(getLength(), true))); } /** * EFFECTS: Produce the big-endian two's complement encoding of the value, in the shortest possible way (i.e., no * leading 0x0 bytes, and no leading 0xFF bytes if negative). Notes, if a positive number is desired (or mandated * like {@link TbsCertificate#getSerialNumber()}, append 0x0 to the MSB manually. This method always results in a * signed integer. For simplicity, the first 0x0 is always removed except when the number itself is 0, and others * are kept. */ @Override public Byte[] encodeValueDER() { Byte[] bytes = Utils.byteToByte(value.toByteArray()); if (bytes.length == 1) { return bytes; } if (bytes[0] == 0x0) { return Arrays.stream(bytes) .skip(1) .toArray(Byte[]::new); } return bytes; } /** * EFFECTS: Get the value in long. * Throws {@link ArithmeticException} if the value is too large for long. */ public long getLong() throws ArithmeticException { return value.longValueExact(); } public BigInteger getValue() { return value; } }