|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.x509; |
|
|
|
import java.io.*; |
|
import java.util.*; |
|
|
|
import sun.security.util.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class GeneralSubtrees implements Cloneable { |
|
|
|
private final List<GeneralSubtree> trees; |
|
|
|
|
|
private static final int NAME_DIFF_TYPE = GeneralNameInterface.NAME_DIFF_TYPE; |
|
private static final int NAME_MATCH = GeneralNameInterface.NAME_MATCH; |
|
private static final int NAME_NARROWS = GeneralNameInterface.NAME_NARROWS; |
|
private static final int NAME_WIDENS = GeneralNameInterface.NAME_WIDENS; |
|
private static final int NAME_SAME_TYPE = GeneralNameInterface.NAME_SAME_TYPE; |
|
|
|
|
|
|
|
*/ |
|
public GeneralSubtrees() { |
|
trees = new ArrayList<>(); |
|
} |
|
|
|
private GeneralSubtrees(GeneralSubtrees source) { |
|
trees = new ArrayList<>(source.trees); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public GeneralSubtrees(DerValue val) throws IOException { |
|
this(); |
|
if (val.tag != DerValue.tag_Sequence) { |
|
throw new IOException("Invalid encoding of GeneralSubtrees."); |
|
} |
|
while (val.data.available() != 0) { |
|
DerValue opt = val.data.getDerValue(); |
|
GeneralSubtree tree = new GeneralSubtree(opt); |
|
add(tree); |
|
} |
|
} |
|
|
|
public GeneralSubtree get(int index) { |
|
return trees.get(index); |
|
} |
|
|
|
public void remove(int index) { |
|
trees.remove(index); |
|
} |
|
|
|
public void add(GeneralSubtree tree) { |
|
if (tree == null) { |
|
throw new NullPointerException(); |
|
} |
|
trees.add(tree); |
|
} |
|
|
|
public boolean contains(GeneralSubtree tree) { |
|
if (tree == null) { |
|
throw new NullPointerException(); |
|
} |
|
return trees.contains(tree); |
|
} |
|
|
|
public int size() { |
|
return trees.size(); |
|
} |
|
|
|
public Iterator<GeneralSubtree> iterator() { |
|
return trees.iterator(); |
|
} |
|
|
|
public List<GeneralSubtree> trees() { |
|
return trees; |
|
} |
|
|
|
public Object clone() { |
|
return new GeneralSubtrees(this); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String toString() { |
|
return " GeneralSubtrees:\n" + trees + '\n'; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void encode(DerOutputStream out) throws IOException { |
|
DerOutputStream seq = new DerOutputStream(); |
|
|
|
for (int i = 0, n = size(); i < n; i++) { |
|
get(i).encode(seq); |
|
} |
|
out.write(DerValue.tag_Sequence, seq); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean equals(Object obj) { |
|
if (this == obj) { |
|
return true; |
|
} |
|
if (obj instanceof GeneralSubtrees == false) { |
|
return false; |
|
} |
|
GeneralSubtrees other = (GeneralSubtrees)obj; |
|
return this.trees.equals(other.trees); |
|
} |
|
|
|
public int hashCode() { |
|
return trees.hashCode(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private GeneralNameInterface getGeneralNameInterface(int ndx) { |
|
return getGeneralNameInterface(get(ndx)); |
|
} |
|
|
|
private static GeneralNameInterface getGeneralNameInterface(GeneralSubtree gs) { |
|
GeneralName gn = gs.getName(); |
|
GeneralNameInterface gni = gn.getName(); |
|
return gni; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void minimize() { |
|
|
|
// Algorithm: compare each entry n to all subsequent entries in |
|
// the list: if any subsequent entry matches or widens entry n, |
|
// remove entry n. If any subsequent entries narrow entry n, remove |
|
|
|
for (int i = 0; i < (size() - 1); i++) { |
|
GeneralNameInterface current = getGeneralNameInterface(i); |
|
boolean remove1 = false; |
|
|
|
|
|
for (int j = i + 1; j < size(); j++) { |
|
GeneralNameInterface subsequent = getGeneralNameInterface(j); |
|
switch (current.constrains(subsequent)) { |
|
case GeneralNameInterface.NAME_DIFF_TYPE: |
|
|
|
continue; |
|
case GeneralNameInterface.NAME_MATCH: |
|
|
|
remove1 = true; |
|
break; |
|
case GeneralNameInterface.NAME_NARROWS: |
|
/* subsequent narrows current */ |
|
|
|
remove(j); |
|
j--; |
|
continue; |
|
case GeneralNameInterface.NAME_WIDENS: |
|
/* subsequent widens current */ |
|
|
|
remove1 = true; |
|
break; |
|
case GeneralNameInterface.NAME_SAME_TYPE: |
|
|
|
continue; |
|
} |
|
break; |
|
} /* end of this pass of subsequent elements */ |
|
|
|
if (remove1) { |
|
remove(i); |
|
i--; /* check the new i value */ |
|
} |
|
|
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private GeneralSubtree createWidestSubtree(GeneralNameInterface name) { |
|
try { |
|
GeneralName newName; |
|
switch (name.getType()) { |
|
case GeneralNameInterface.NAME_ANY: |
|
// Create new OtherName with same OID as baseName, but |
|
|
|
ObjectIdentifier otherOID = ((OtherName)name).getOID(); |
|
newName = new GeneralName(new OtherName(otherOID, null)); |
|
break; |
|
case GeneralNameInterface.NAME_RFC822: |
|
newName = new GeneralName(new RFC822Name("")); |
|
break; |
|
case GeneralNameInterface.NAME_DNS: |
|
newName = new GeneralName(new DNSName("")); |
|
break; |
|
case GeneralNameInterface.NAME_X400: |
|
newName = new GeneralName(new X400Address((byte[])null)); |
|
break; |
|
case GeneralNameInterface.NAME_DIRECTORY: |
|
newName = new GeneralName(new X500Name("")); |
|
break; |
|
case GeneralNameInterface.NAME_EDI: |
|
newName = new GeneralName(new EDIPartyName("")); |
|
break; |
|
case GeneralNameInterface.NAME_URI: |
|
newName = new GeneralName(new URIName("")); |
|
break; |
|
case GeneralNameInterface.NAME_IP: |
|
newName = new GeneralName(new IPAddressName((byte[])null)); |
|
break; |
|
case GeneralNameInterface.NAME_OID: |
|
newName = new GeneralName |
|
(new OIDName(new ObjectIdentifier((int[])null))); |
|
break; |
|
default: |
|
throw new IOException |
|
("Unsupported GeneralNameInterface type: " + name.getType()); |
|
} |
|
return new GeneralSubtree(newName, 0, -1); |
|
} catch (IOException e) { |
|
throw new RuntimeException("Unexpected error: " + e, e); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public GeneralSubtrees intersect(GeneralSubtrees other) { |
|
|
|
if (other == null) { |
|
throw new NullPointerException("other GeneralSubtrees must not be null"); |
|
} |
|
|
|
GeneralSubtrees newThis = new GeneralSubtrees(); |
|
GeneralSubtrees newExcluded = null; |
|
|
|
// Step 1: If this is empty, just add everything in other to this and |
|
|
|
if (size() == 0) { |
|
union(other); |
|
return null; |
|
} |
|
|
|
// Step 2: For ease of checking the subtrees, minimize them by |
|
// constructing versions that contain only the widest instance of |
|
|
|
this.minimize(); |
|
other.minimize(); |
|
|
|
// Step 3: Check each entry in this to see whether we keep it or |
|
// remove it, and whether we add anything to newExcluded or newThis. |
|
// We keep an entry in this unless it is narrowed by all entries in |
|
// other. We add an entry to newExcluded if there is at least one |
|
// entry of the same nameType in other, but this entry does |
|
// not share the same subtree with any of the entries in other. |
|
// We add an entry from other to newThis if there is no name of the |
|
|
|
for (int i = 0; i < size(); i++) { |
|
GeneralNameInterface thisEntry = getGeneralNameInterface(i); |
|
boolean removeThisEntry = false; |
|
|
|
// Step 3a: If the widest name of this type in other narrows |
|
// thisEntry, remove thisEntry and add widest other to newThis. |
|
// Simultaneously, check for situation where there is a name of |
|
// this type in other, but no name in other matches, narrows, |
|
|
|
boolean sameType = false; |
|
for (int j = 0; j < other.size(); j++) { |
|
GeneralSubtree otherEntryGS = other.get(j); |
|
GeneralNameInterface otherEntry = |
|
getGeneralNameInterface(otherEntryGS); |
|
switch (thisEntry.constrains(otherEntry)) { |
|
case NAME_NARROWS: |
|
remove(i); |
|
i--; |
|
newThis.add(otherEntryGS); |
|
sameType = false; |
|
break; |
|
case NAME_SAME_TYPE: |
|
sameType = true; |
|
continue; |
|
case NAME_MATCH: |
|
case NAME_WIDENS: |
|
sameType = false; |
|
break; |
|
case NAME_DIFF_TYPE: |
|
default: |
|
continue; |
|
} |
|
break; |
|
} |
|
|
|
// Step 3b: If sameType is still true, we have the situation |
|
// where there was a name of the same type as thisEntry in |
|
// other, but no name in other widened, matched, or narrowed |
|
|
|
if (sameType) { |
|
|
|
// Step 3b.1: See if there are any entries in this and other |
|
// with this type that match, widen, or narrow each other. |
|
// If not, then we need to add a "widest subtree" of this |
|
|
|
boolean intersection = false; |
|
for (int j = 0; j < size(); j++) { |
|
GeneralNameInterface thisAltEntry = getGeneralNameInterface(j); |
|
|
|
if (thisAltEntry.getType() == thisEntry.getType()) { |
|
for (int k = 0; k < other.size(); k++) { |
|
GeneralNameInterface othAltEntry = |
|
other.getGeneralNameInterface(k); |
|
|
|
int constraintType = |
|
thisAltEntry.constrains(othAltEntry); |
|
if (constraintType == NAME_MATCH || |
|
constraintType == NAME_WIDENS || |
|
constraintType == NAME_NARROWS) { |
|
intersection = true; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
if (intersection == false) { |
|
if (newExcluded == null) { |
|
newExcluded = new GeneralSubtrees(); |
|
} |
|
GeneralSubtree widestSubtree = |
|
createWidestSubtree(thisEntry); |
|
if (!newExcluded.contains(widestSubtree)) { |
|
newExcluded.add(widestSubtree); |
|
} |
|
} |
|
|
|
|
|
remove(i); |
|
i--; |
|
} |
|
} |
|
|
|
|
|
if (newThis.size() > 0) { |
|
union(newThis); |
|
} |
|
|
|
// Step 5: Add all entries in other that do not have any entry of the |
|
|
|
for (int i = 0; i < other.size(); i++) { |
|
GeneralSubtree otherEntryGS = other.get(i); |
|
GeneralNameInterface otherEntry = getGeneralNameInterface(otherEntryGS); |
|
boolean diffType = false; |
|
for (int j = 0; j < size(); j++) { |
|
GeneralNameInterface thisEntry = getGeneralNameInterface(j); |
|
switch (thisEntry.constrains(otherEntry)) { |
|
case NAME_DIFF_TYPE: |
|
diffType = true; |
|
// continue to see if we find something later of the |
|
|
|
continue; |
|
case NAME_NARROWS: |
|
case NAME_SAME_TYPE: |
|
case NAME_MATCH: |
|
case NAME_WIDENS: |
|
diffType = false; |
|
// break because we know we won't be adding it to |
|
|
|
break; |
|
default: |
|
continue; |
|
} |
|
break; |
|
} |
|
if (diffType) { |
|
add(otherEntryGS); |
|
} |
|
} |
|
|
|
|
|
return newExcluded; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void union(GeneralSubtrees other) { |
|
if (other != null) { |
|
for (int i = 0, n = other.size(); i < n; i++) { |
|
add(other.get(i)); |
|
} |
|
|
|
minimize(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void reduce(GeneralSubtrees excluded) { |
|
if (excluded == null) { |
|
return; |
|
} |
|
for (int i = 0, n = excluded.size(); i < n; i++) { |
|
GeneralNameInterface excludedName = excluded.getGeneralNameInterface(i); |
|
for (int j = 0; j < size(); j++) { |
|
GeneralNameInterface permitted = getGeneralNameInterface(j); |
|
switch (excludedName.constrains(permitted)) { |
|
case GeneralNameInterface.NAME_DIFF_TYPE: |
|
break; |
|
case GeneralNameInterface.NAME_MATCH: |
|
remove(j); |
|
j--; |
|
break; |
|
case GeneralNameInterface.NAME_NARROWS: |
|
|
|
remove(j); |
|
j--; |
|
break; |
|
case GeneralNameInterface.NAME_WIDENS: |
|
|
|
break; |
|
case GeneralNameInterface.NAME_SAME_TYPE: |
|
break; |
|
} |
|
} /* end of this pass of permitted */ |
|
} /* end of pass of excluded */ |
|
} |
|
} |