Path: blob/master/src/java.base/share/classes/sun/security/x509/GeneralSubtrees.java
41159 views
/*1* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.x509;2627import java.io.*;28import java.util.*;2930import sun.security.util.*;3132/**33* Represent the GeneralSubtrees ASN.1 object.34* <p>35* The ASN.1 for this is36* <pre>37* GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree38* </pre>39*40*41* @author Amit Kapoor42* @author Hemma Prafullchandra43* @author Andreas Sterbenz44*/45public class GeneralSubtrees implements Cloneable {4647private final List<GeneralSubtree> trees;4849// Private variables50private static final int NAME_DIFF_TYPE = GeneralNameInterface.NAME_DIFF_TYPE;51private static final int NAME_MATCH = GeneralNameInterface.NAME_MATCH;52private static final int NAME_NARROWS = GeneralNameInterface.NAME_NARROWS;53private static final int NAME_WIDENS = GeneralNameInterface.NAME_WIDENS;54private static final int NAME_SAME_TYPE = GeneralNameInterface.NAME_SAME_TYPE;5556/**57* The default constructor for the class.58*/59public GeneralSubtrees() {60trees = new ArrayList<>();61}6263private GeneralSubtrees(GeneralSubtrees source) {64trees = new ArrayList<>(source.trees);65}6667/**68* Create the object from the passed DER encoded form.69*70* @param val the DER encoded form of the same.71*/72public GeneralSubtrees(DerValue val) throws IOException {73this();74if (val.tag != DerValue.tag_Sequence) {75throw new IOException("Invalid encoding of GeneralSubtrees.");76}77while (val.data.available() != 0) {78DerValue opt = val.data.getDerValue();79GeneralSubtree tree = new GeneralSubtree(opt);80add(tree);81}82}8384public GeneralSubtree get(int index) {85return trees.get(index);86}8788public void remove(int index) {89trees.remove(index);90}9192public void add(GeneralSubtree tree) {93if (tree == null) {94throw new NullPointerException();95}96trees.add(tree);97}9899public boolean contains(GeneralSubtree tree) {100if (tree == null) {101throw new NullPointerException();102}103return trees.contains(tree);104}105106public int size() {107return trees.size();108}109110public Iterator<GeneralSubtree> iterator() {111return trees.iterator();112}113114public List<GeneralSubtree> trees() {115return trees;116}117118public Object clone() {119return new GeneralSubtrees(this);120}121122/**123* Return a printable string of the GeneralSubtree.124*/125public String toString() {126return " GeneralSubtrees:\n" + trees + '\n';127}128129/**130* Encode the GeneralSubtrees.131*132* @param out the DerOutputStrean to encode this object to.133*/134public void encode(DerOutputStream out) throws IOException {135DerOutputStream seq = new DerOutputStream();136137for (int i = 0, n = size(); i < n; i++) {138get(i).encode(seq);139}140out.write(DerValue.tag_Sequence, seq);141}142143/**144* Compare two general subtrees by comparing the subtrees145* of each.146*147* @param obj GeneralSubtrees to compare to this148* @return true if match149*/150public boolean equals(Object obj) {151if (this == obj) {152return true;153}154if (obj instanceof GeneralSubtrees == false) {155return false;156}157GeneralSubtrees other = (GeneralSubtrees)obj;158return this.trees.equals(other.trees);159}160161public int hashCode() {162return trees.hashCode();163}164165/**166* Return the GeneralNameInterface form of the GeneralName in one of167* the GeneralSubtrees.168*169* @param ndx index of the GeneralSubtree from which to obtain the name170*/171private GeneralNameInterface getGeneralNameInterface(int ndx) {172return getGeneralNameInterface(get(ndx));173}174175private static GeneralNameInterface getGeneralNameInterface(GeneralSubtree gs) {176GeneralName gn = gs.getName();177GeneralNameInterface gni = gn.getName();178return gni;179}180181/**182* minimize this GeneralSubtrees by removing all redundant entries.183* Internal method used by intersect and reduce.184*/185private void minimize() {186187// Algorithm: compare each entry n to all subsequent entries in188// the list: if any subsequent entry matches or widens entry n,189// remove entry n. If any subsequent entries narrow entry n, remove190// the subsequent entries.191for (int i = 0; i < (size() - 1); i++) {192GeneralNameInterface current = getGeneralNameInterface(i);193boolean remove1 = false;194195/* compare current to subsequent elements */196for (int j = i + 1; j < size(); j++) {197GeneralNameInterface subsequent = getGeneralNameInterface(j);198switch (current.constrains(subsequent)) {199case GeneralNameInterface.NAME_DIFF_TYPE:200/* not comparable; different name types; keep checking */201continue;202case GeneralNameInterface.NAME_MATCH:203/* delete one of the duplicates */204remove1 = true;205break;206case GeneralNameInterface.NAME_NARROWS:207/* subsequent narrows current */208/* remove narrower name (subsequent) */209remove(j);210j--; /* continue check with new subsequent */211continue;212case GeneralNameInterface.NAME_WIDENS:213/* subsequent widens current */214/* remove narrower name current */215remove1 = true;216break;217case GeneralNameInterface.NAME_SAME_TYPE:218/* keep both for now; keep checking */219continue;220}221break;222} /* end of this pass of subsequent elements */223224if (remove1) {225remove(i);226i--; /* check the new i value */227}228229}230}231232/**233* create a subtree containing an instance of the input234* name type that widens all other names of that type.235*236* @param name GeneralNameInterface name237* @return GeneralSubtree containing widest name of that type238* @throws RuntimeException on error (should not occur)239*/240private GeneralSubtree createWidestSubtree(GeneralNameInterface name) {241try {242GeneralName newName;243switch (name.getType()) {244case GeneralNameInterface.NAME_ANY:245// Create new OtherName with same OID as baseName, but246// empty value247ObjectIdentifier otherOID = ((OtherName)name).getOID();248newName = new GeneralName(new OtherName(otherOID, null));249break;250case GeneralNameInterface.NAME_RFC822:251newName = new GeneralName(new RFC822Name(""));252break;253case GeneralNameInterface.NAME_DNS:254newName = new GeneralName(new DNSName(""));255break;256case GeneralNameInterface.NAME_X400:257newName = new GeneralName(new X400Address((byte[])null));258break;259case GeneralNameInterface.NAME_DIRECTORY:260newName = new GeneralName(new X500Name(""));261break;262case GeneralNameInterface.NAME_EDI:263newName = new GeneralName(new EDIPartyName(""));264break;265case GeneralNameInterface.NAME_URI:266newName = new GeneralName(new URIName(""));267break;268case GeneralNameInterface.NAME_IP:269newName = new GeneralName(new IPAddressName((byte[])null));270break;271case GeneralNameInterface.NAME_OID:272newName = new GeneralName(new OIDName(""));273break;274default:275throw new IOException276("Unsupported GeneralNameInterface type: " + name.getType());277}278return new GeneralSubtree(newName, 0, -1);279} catch (IOException e) {280throw new RuntimeException("Unexpected error: " + e, e);281}282}283284/**285* intersect this GeneralSubtrees with other. This function286* is used in merging permitted NameConstraints. The operation287* is performed as follows:288* <ul>289* <li>If a name in other narrows all names of the same type in this,290* the result will contain the narrower name and none of the291* names it narrows.292* <li>If a name in other widens all names of the same type in this,293* the result will not contain the wider name.294* <li>If a name in other does not share the same subtree with any name295* of the same type in this, then the name is added to the list296* of GeneralSubtrees returned. These names should be added to297* the list of names that are specifically excluded. The reason298* is that, if the intersection is empty, then no names of that299* type are permitted, and the only way to express this in300* NameConstraints is to include the name in excludedNames.301* <li>If a name in this has no name of the same type in other, then302* the result contains the name in this. No name of a given type303* means the name type is completely permitted.304* <li>If a name in other has no name of the same type in this, then305* the result contains the name in other. This means that306* the name is now constrained in some way, whereas before it was307* completely permitted.308* </ul>309*310* @param other GeneralSubtrees to be intersected with this311* @return GeneralSubtrees to be merged with excluded; these are312* empty-valued name types corresponding to entries that were313* of the same type but did not share the same subtree between314* this and other. Returns null if no such.315*/316public GeneralSubtrees intersect(GeneralSubtrees other) {317318if (other == null) {319throw new NullPointerException("other GeneralSubtrees must not be null");320}321322GeneralSubtrees newThis = new GeneralSubtrees();323GeneralSubtrees newExcluded = null;324325// Step 1: If this is empty, just add everything in other to this and326// return no new excluded entries327if (size() == 0) {328union(other);329return null;330}331332// Step 2: For ease of checking the subtrees, minimize them by333// constructing versions that contain only the widest instance of334// each type335this.minimize();336other.minimize();337338// Step 3: Check each entry in this to see whether we keep it or339// remove it, and whether we add anything to newExcluded or newThis.340// We keep an entry in this unless it is narrowed by all entries in341// other. We add an entry to newExcluded if there is at least one342// entry of the same nameType in other, but this entry does343// not share the same subtree with any of the entries in other.344// We add an entry from other to newThis if there is no name of the345// same type in this.346for (int i = 0; i < size(); i++) {347GeneralNameInterface thisEntry = getGeneralNameInterface(i);348boolean removeThisEntry = false;349350// Step 3a: If the widest name of this type in other narrows351// thisEntry, remove thisEntry and add widest other to newThis.352// Simultaneously, check for situation where there is a name of353// this type in other, but no name in other matches, narrows,354// or widens thisEntry.355boolean sameType = false;356for (int j = 0; j < other.size(); j++) {357GeneralSubtree otherEntryGS = other.get(j);358GeneralNameInterface otherEntry =359getGeneralNameInterface(otherEntryGS);360switch (thisEntry.constrains(otherEntry)) {361case NAME_NARROWS:362remove(i);363i--;364newThis.add(otherEntryGS);365sameType = false;366break;367case NAME_SAME_TYPE:368sameType = true;369continue;370case NAME_MATCH:371case NAME_WIDENS:372sameType = false;373break;374case NAME_DIFF_TYPE:375default:376continue;377}378break;379}380381// Step 3b: If sameType is still true, we have the situation382// where there was a name of the same type as thisEntry in383// other, but no name in other widened, matched, or narrowed384// thisEntry.385if (sameType) {386387// Step 3b.1: See if there are any entries in this and other388// with this type that match, widen, or narrow each other.389// If not, then we need to add a "widest subtree" of this390// type to excluded.391boolean intersection = false;392for (int j = 0; j < size(); j++) {393GeneralNameInterface thisAltEntry = getGeneralNameInterface(j);394395if (thisAltEntry.getType() == thisEntry.getType()) {396for (int k = 0; k < other.size(); k++) {397GeneralNameInterface othAltEntry =398other.getGeneralNameInterface(k);399400int constraintType =401thisAltEntry.constrains(othAltEntry);402if (constraintType == NAME_MATCH ||403constraintType == NAME_WIDENS ||404constraintType == NAME_NARROWS) {405intersection = true;406break;407}408}409}410}411if (intersection == false) {412if (newExcluded == null) {413newExcluded = new GeneralSubtrees();414}415GeneralSubtree widestSubtree =416createWidestSubtree(thisEntry);417if (!newExcluded.contains(widestSubtree)) {418newExcluded.add(widestSubtree);419}420}421422// Step 3b.2: Remove thisEntry from this423remove(i);424i--;425}426}427428// Step 4: Add all entries in newThis to this429if (newThis.size() > 0) {430union(newThis);431}432433// Step 5: Add all entries in other that do not have any entry of the434// same type in this to this435for (int i = 0; i < other.size(); i++) {436GeneralSubtree otherEntryGS = other.get(i);437GeneralNameInterface otherEntry = getGeneralNameInterface(otherEntryGS);438boolean diffType = false;439for (int j = 0; j < size(); j++) {440GeneralNameInterface thisEntry = getGeneralNameInterface(j);441switch (thisEntry.constrains(otherEntry)) {442case NAME_DIFF_TYPE:443diffType = true;444// continue to see if we find something later of the445// same type446continue;447case NAME_NARROWS:448case NAME_SAME_TYPE:449case NAME_MATCH:450case NAME_WIDENS:451diffType = false; // we found an entry of the same type452// break because we know we won't be adding it to453// this now454break;455default:456continue;457}458break;459}460if (diffType) {461add(otherEntryGS);462}463}464465// Step 6: Return the newExcluded GeneralSubtrees466return newExcluded;467}468469/**470* construct union of this GeneralSubtrees with other.471*472* @param other GeneralSubtrees to be united with this473*/474public void union(GeneralSubtrees other) {475if (other != null) {476for (int i = 0, n = other.size(); i < n; i++) {477add(other.get(i));478}479// Minimize this480minimize();481}482}483484/**485* reduce this GeneralSubtrees by contents of another. This function486* is used in merging excluded NameConstraints with permitted NameConstraints487* to obtain a minimal form of permitted NameConstraints. It is an488* optimization, and does not affect correctness of the results.489*490* @param excluded GeneralSubtrees491*/492public void reduce(GeneralSubtrees excluded) {493if (excluded == null) {494return;495}496for (int i = 0, n = excluded.size(); i < n; i++) {497GeneralNameInterface excludedName = excluded.getGeneralNameInterface(i);498for (int j = 0; j < size(); j++) {499GeneralNameInterface permitted = getGeneralNameInterface(j);500switch (excludedName.constrains(permitted)) {501case GeneralNameInterface.NAME_DIFF_TYPE:502break;503case GeneralNameInterface.NAME_MATCH:504remove(j);505j--;506break;507case GeneralNameInterface.NAME_NARROWS:508/* permitted narrows excluded */509remove(j);510j--;511break;512case GeneralNameInterface.NAME_WIDENS:513/* permitted widens excluded */514break;515case GeneralNameInterface.NAME_SAME_TYPE:516break;517}518} /* end of this pass of permitted */519} /* end of pass of excluded */520}521}522523524