8246797: A convenient method to read OPTIONAL element

Reviewed-by: jnimeh, valeriep
This commit is contained in:
Weijun Wang 2021-09-13 18:05:27 +00:00
parent 6cf5079d8e
commit fc0f8542c3
6 changed files with 276 additions and 96 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,6 +30,8 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
import java.util.function.Predicate;
/**
* A DER input stream, used for parsing ASN.1 DER-encoded data such as
@ -53,7 +55,6 @@ import java.util.Date;
* @author Amit Kapoor
* @author Hemma Prafullchandra
*/
public class DerInputStream {
// The static part
@ -305,4 +306,105 @@ public class DerInputStream {
* empty.
*/
public int available() { return end - pos; }
/**
* Ensures there is no more data. This can be called when the last
* expected field is parsed, and we need to make sure no unread is left.
*
* @throws IOException if the end is NOT reached yet
*/
public void atEnd() throws IOException {
if (available() != 0) {
throw new IOException("Extra unused bytes");
}
}
/**
* Checks if the tag of the next DerValue matches the rule.
*
* @param rule the rule to check for the tag.
* @return true if matches, false if not or stream is at end.
* @throws IOException if an I/O error happens while peeking the byte
*/
private boolean checkNextTag(Predicate<Byte> rule) {
return available() > 0 && rule.test(data[pos]);
}
/**
* Detect if the tag of the next DerValue is the specified one.
*
* @param tag the expected tag
* @return true if matches, false if not or stream is at end.
* @throws IOException if an I/O error happens while peeking the byte
*/
private boolean checkNextTag(byte tag) {
return checkNextTag(t -> t == tag);
}
/**
* Returns the next DerValue if its tag is the given one.
*
* @param tag the expected tag
* @return the next DerValue, or empty if not found or stream at end
* @throws IOException if an I/O error happens
*/
public Optional<DerValue> getOptional(byte tag) throws IOException {
if (checkNextTag(tag)) {
return Optional.of(getDerValue());
} else {
return Optional.empty();
}
}
/**
* Detect if the next DerValue is a context-specific value
* tagged by {@code n}.
*
* @param n the expected tag
* @return true if matches, false if not or stream is at end.
* @throws IOException if an I/O error happens while peeking the byte
*/
public boolean seeOptionalContextSpecific(int n) throws IOException {
return checkNextTag(t -> (t & 0x0c0) == 0x080 && (t & 0x01f) == n);
}
/**
* Returns the inner DerValue if the next DerValue is
* an EXPLICIT context-specific value tagged by {@code n}.
*
* @param n the expected tag
* @return the inner DerValue, or empty if not found or stream at end
* @throws IOException if an I/O error happens
*/
public Optional<DerValue> getOptionalExplicitContextSpecific(int n)
throws IOException {
if (seeOptionalContextSpecific(n)) {
DerInputStream sub = getDerValue().data(); // stream inside [n]
DerValue inner = sub.getDerValue(); // inside [n]
sub.atEnd(); // make sure there is only one inner value
return Optional.of(inner);
} else {
return Optional.empty();
}
}
/**
* Returns the restored DerValue if the next DerValue is
* an IMPLICIT context-specific value tagged by {@code n}.
*
* @param n the expected tag
* @param tag the real tag for the IMPLICIT type
* @return the restored DerValue, or empty if not found or stream at end
* @throws IOException if an I/O error happens
*/
public Optional<DerValue> getOptionalImplicitContextSpecific(int n, byte tag)
throws IOException {
if (seeOptionalContextSpecific(n)) {
DerValue v = getDerValue(); // [n]
// restore tag because IMPLICIT has overwritten it
return Optional.of(v.withTag(tag));
} else {
return Optional.empty();
}
}
}

View file

@ -290,7 +290,7 @@ public class DerValue {
}
/**
* Wraps an DerOutputStream. All bytes currently written
* Wraps a DerOutputStream. All bytes currently written
* into the stream will become the content of the newly
* created DerValue.
*
@ -305,6 +305,34 @@ public class DerValue {
return new DerValue(tag, out.buf(), 0, out.size(), false);
}
/**
* Wraps a byte array as a single DerValue.
*
* Attention: no cloning is made.
*
* @param buf the byte array containing the DER-encoded datum
* @returns a new DerValue
*/
public static DerValue wrap(byte[] buf)
throws IOException {
return wrap(buf, 0, buf.length);
}
/**
* Wraps a byte array as a single DerValue.
*
* Attention: no cloning is made.
*
* @param buf the byte array containing the DER-encoded datum
* @param offset where the encoded datum starts inside {@code buf}
* @param len length of bytes to parse inside {@code buf}
* @returns a new DerValue
*/
public static DerValue wrap(byte[] buf, int offset, int len)
throws IOException {
return new DerValue(buf, offset, len, true, false);
}
/**
* Parse an ASN.1/BER encoded datum. The entire encoding must hold exactly
* one datum, including its tag and length.
@ -1229,7 +1257,7 @@ public class DerValue {
* @param startLen estimated number of sub-values
* @return the sub-values in an array
*/
DerValue[] subs(byte expectedTag, int startLen) throws IOException {
public DerValue[] subs(byte expectedTag, int startLen) throws IOException {
if (expectedTag != 0 && expectedTag != tag) {
throw new IOException("Not the correct tag");
}