1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
|
/*
* Copyright (c) 1997, 2003, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.x509;
import java.io.*;
import java.util.*;
import sun.security.util.*;
/**
* Represent the GeneralSubtrees ASN.1 object.
* <p>
* The ASN.1 for this is
* <pre>
* GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
* </pre>
* </p>
*
*
* @author Amit Kapoor
* @author Hemma Prafullchandra
* @author Andreas Sterbenz
*/
public class GeneralSubtrees implements Cloneable {
private final List<GeneralSubtree> trees;
// Private variables
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;
/**
* The default constructor for the class.
*/
public GeneralSubtrees() {
trees = new ArrayList<GeneralSubtree>();
}
private GeneralSubtrees(GeneralSubtrees source) {
trees = new ArrayList<GeneralSubtree>(source.trees);
}
/**
* Create the object from the passed DER encoded form.
*
* @param val the DER encoded form of the same.
*/
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);
}
/**
* Return a printable string of the GeneralSubtree.
*/
public String toString() {
String s = " GeneralSubtrees:\n" + trees.toString() + "\n";
return s;
}
/**
* Encode the GeneralSubtrees.
*
* @params out the DerOutputStrean to encode this object to.
*/
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);
}
/**
* Compare two general subtrees by comparing the subtrees
* of each.
*
* @param other GeneralSubtrees to compare to this
* @returns true if match
*/
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();
}
/**
* Return the GeneralNameInterface form of the GeneralName in one of
* the GeneralSubtrees.
*
* @param ndx index of the GeneralSubtree from which to obtain the name
*/
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;
}
/**
* minimize this GeneralSubtrees by removing all redundant entries.
* Internal method used by intersect and reduce.
*/
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
// the subsequent entries.
for (int i = 0; i < (size() - 1); i++) {
GeneralNameInterface current = getGeneralNameInterface(i);
boolean remove1 = false;
/* compare current to subsequent elements */
for (int j = i + 1; j < size(); j++) {
GeneralNameInterface subsequent = getGeneralNameInterface(j);
switch (current.constrains(subsequent)) {
case GeneralNameInterface.NAME_DIFF_TYPE:
/* not comparable; different name types; keep checking */
continue;
case GeneralNameInterface.NAME_MATCH:
/* delete one of the duplicates */
remove1 = true;
break;
case GeneralNameInterface.NAME_NARROWS:
/* subsequent narrows current */
/* remove narrower name (subsequent) */
remove(j);
j--; /* continue check with new subsequent */
continue;
case GeneralNameInterface.NAME_WIDENS:
/* subsequent widens current */
/* remove narrower name current */
remove1 = true;
break;
case GeneralNameInterface.NAME_SAME_TYPE:
/* keep both for now; keep checking */
continue;
}
break;
} /* end of this pass of subsequent elements */
if (remove1) {
remove(i);
i--; /* check the new i value */
}
}
}
/**
* create a subtree containing an instance of the input
* name type that widens all other names of that type.
*
* @params name GeneralNameInterface name
* @returns GeneralSubtree containing widest name of that type
* @throws RuntimeException on error (should not occur)
*/
private GeneralSubtree createWidestSubtree(GeneralNameInterface name) {
try {
GeneralName newName;
switch (name.getType()) {
case GeneralNameInterface.NAME_ANY:
// Create new OtherName with same OID as baseName, but
// empty value
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);
}
}
/**
* intersect this GeneralSubtrees with other. This function
* is used in merging permitted NameConstraints. The operation
* is performed as follows:
* <ul>
* <li>If a name in other narrows all names of the same type in this,
* the result will contain the narrower name and none of the
* names it narrows.
* <li>If a name in other widens all names of the same type in this,
* the result will not contain the wider name.
* <li>If a name in other does not share the same subtree with any name
* of the same type in this, then the name is added to the list
* of GeneralSubtrees returned. These names should be added to
* the list of names that are specifically excluded. The reason
* is that, if the intersection is empty, then no names of that
* type are permitted, and the only way to express this in
* NameConstraints is to include the name in excludedNames.
* <li>If a name in this has no name of the same type in other, then
* the result contains the name in this. No name of a given type
* means the name type is completely permitted.
* <li>If a name in other has no name of the same type in this, then
* the result contains the name in other. This means that
* the name is now constrained in some way, whereas before it was
* completely permitted.
* <ul>
*
* @param other GeneralSubtrees to be intersected with this
* @returns GeneralSubtrees to be merged with excluded; these are
* empty-valued name types corresponding to entries that were
* of the same type but did not share the same subtree between
* this and other. Returns null if no such.
*/
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
// return no new excluded entries
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
// each type
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
// same type in this.
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,
// or widens thisEntry.
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
// thisEntry.
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
// type to excluded.
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);
}
}
// Step 3b.2: Remove thisEntry from this
remove(i);
i--;
}
}
// Step 4: Add all entries in newThis to this
if (newThis.size() > 0) {
union(newThis);
}
// Step 5: Add all entries in other that do not have any entry of the
// same type in this to this
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
// same type
continue;
case NAME_NARROWS:
case NAME_SAME_TYPE:
case NAME_MATCH:
case NAME_WIDENS:
diffType = false; // we found an entry of the same type
// break because we know we won't be adding it to
// this now
break;
default:
continue;
}
break;
}
if (diffType) {
add(otherEntryGS);
}
}
// Step 6: Return the newExcluded GeneralSubtrees
return newExcluded;
}
/**
* construct union of this GeneralSubtrees with other.
*
* @param other GeneralSubtrees to be united with this
*/
public void union(GeneralSubtrees other) {
if (other != null) {
for (int i = 0, n = other.size(); i < n; i++) {
add(other.get(i));
}
// Minimize this
minimize();
}
}
/**
* reduce this GeneralSubtrees by contents of another. This function
* is used in merging excluded NameConstraints with permitted NameConstraints
* to obtain a minimal form of permitted NameConstraints. It is an
* optimization, and does not affect correctness of the results.
*
* @param excluded GeneralSubtrees
*/
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:
/* permitted narrows excluded */
remove(j);
j--;
break;
case GeneralNameInterface.NAME_WIDENS:
/* permitted widens excluded */
break;
case GeneralNameInterface.NAME_SAME_TYPE:
break;
}
} /* end of this pass of permitted */
} /* end of pass of excluded */
}
}
|