Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java
41152 views
1
/*
2
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package javax.crypto;
27
28
import java.io.*;
29
import java.util.Enumeration;
30
import java.util.Hashtable;
31
import java.util.Vector;
32
import static java.util.Locale.ENGLISH;
33
34
import java.security.GeneralSecurityException;
35
import java.security.spec.AlgorithmParameterSpec;
36
import java.lang.reflect.*;
37
38
/**
39
* JCE has two pairs of jurisdiction policy files: one represents U.S. export
40
* laws, and the other represents the local laws of the country where the
41
* JCE will be used.
42
*
43
* The jurisdiction policy file has the same syntax as JDK policy files except
44
* that JCE has new permission classes called javax.crypto.CryptoPermission
45
* and javax.crypto.CryptoAllPermission.
46
*
47
* The format of a permission entry in the jurisdiction policy file is:
48
*
49
* <pre>{@code
50
* permission <crypto permission class name>[, <algorithm name>
51
* [[, <exemption mechanism name>][, <maxKeySize>
52
* [, <AlgorithmParameterSpec class name>, <parameters
53
* for constructing an AlgorithmParameterSpec object>]]]];
54
* }</pre>
55
*
56
* @author Sharon Liu
57
*
58
* @see java.security.Permissions
59
* @see java.security.spec.AlgorithmParameterSpec
60
* @see javax.crypto.CryptoPermission
61
* @see javax.crypto.CryptoAllPermission
62
* @see javax.crypto.CryptoPermissions
63
* @since 1.4
64
*/
65
66
final class CryptoPolicyParser {
67
68
private Vector<GrantEntry> grantEntries;
69
70
// Convenience variables for parsing
71
private StreamTokenizer st;
72
private int lookahead;
73
74
/**
75
* Creates a CryptoPolicyParser object.
76
*/
77
CryptoPolicyParser() {
78
grantEntries = new Vector<GrantEntry>();
79
}
80
81
/**
82
* Reads a policy configuration using a Reader object. <p>
83
*
84
* @param policy the policy Reader object.
85
*
86
* @exception ParsingException if the policy configuration
87
* contains a syntax error.
88
*
89
* @exception IOException if an error occurs while reading
90
* the policy configuration.
91
*/
92
93
void read(Reader policy)
94
throws ParsingException, IOException
95
{
96
if (!(policy instanceof BufferedReader)) {
97
policy = new BufferedReader(policy);
98
}
99
100
/*
101
* Configure the stream tokenizer:
102
* Recognize strings between "..."
103
* Don't convert words to lowercase
104
* Recognize both C-style and C++-style comments
105
* Treat end-of-line as white space, not as a token
106
*/
107
st = new StreamTokenizer(policy);
108
109
st.resetSyntax();
110
st.wordChars('a', 'z');
111
st.wordChars('A', 'Z');
112
st.wordChars('.', '.');
113
st.wordChars('0', '9');
114
st.wordChars('_', '_');
115
st.wordChars('$', '$');
116
st.wordChars(128 + 32, 255);
117
st.whitespaceChars(0, ' ');
118
st.commentChar('/');
119
st.quoteChar('\'');
120
st.quoteChar('"');
121
st.lowerCaseMode(false);
122
st.ordinaryChar('/');
123
st.slashSlashComments(true);
124
st.slashStarComments(true);
125
st.parseNumbers();
126
127
/*
128
* The crypto jurisdiction policy must be consistent. The
129
* following hashtable is used for checking consistency.
130
*/
131
Hashtable<String, Vector<String>> processedPermissions = null;
132
133
/*
134
* The main parsing loop. The loop is executed once for each entry
135
* in the policy file. The entries are delimited by semicolons. Once
136
* we've read in the information for an entry, go ahead and try to
137
* add it to the grantEntries.
138
*/
139
lookahead = st.nextToken();
140
while (lookahead != StreamTokenizer.TT_EOF) {
141
if (peek("grant")) {
142
GrantEntry ge = parseGrantEntry(processedPermissions);
143
if (ge != null)
144
grantEntries.addElement(ge);
145
} else {
146
throw new ParsingException(st.lineno(), "expected grant " +
147
"statement");
148
}
149
match(";");
150
}
151
}
152
153
/**
154
* parse a Grant entry
155
*/
156
private GrantEntry parseGrantEntry(
157
Hashtable<String, Vector<String>> processedPermissions)
158
throws ParsingException, IOException
159
{
160
GrantEntry e = new GrantEntry();
161
162
match("grant");
163
match("{");
164
165
while(!peek("}")) {
166
if (peek("Permission")) {
167
CryptoPermissionEntry pe =
168
parsePermissionEntry(processedPermissions);
169
e.add(pe);
170
match(";");
171
} else {
172
throw new
173
ParsingException(st.lineno(), "expected permission entry");
174
}
175
}
176
match("}");
177
178
return e;
179
}
180
181
/**
182
* parse a CryptoPermission entry
183
*/
184
private CryptoPermissionEntry parsePermissionEntry(
185
Hashtable<String, Vector<String>> processedPermissions)
186
throws ParsingException, IOException
187
{
188
CryptoPermissionEntry e = new CryptoPermissionEntry();
189
190
match("Permission");
191
e.cryptoPermission = match("permission type");
192
193
if (e.cryptoPermission.equals("javax.crypto.CryptoAllPermission")) {
194
// Done with the CryptoAllPermission entry.
195
e.alg = CryptoAllPermission.ALG_NAME;
196
e.maxKeySize = Integer.MAX_VALUE;
197
return e;
198
}
199
200
// Should see the algorithm name.
201
if (peek("\"")) {
202
// Algorithm name - always convert to upper case after parsing.
203
e.alg = match("quoted string").toUpperCase(ENGLISH);
204
} else {
205
// The algorithm name can be a wildcard.
206
if (peek("*")) {
207
match("*");
208
e.alg = CryptoPermission.ALG_NAME_WILDCARD;
209
} else {
210
throw new ParsingException(st.lineno(),
211
"Missing the algorithm name");
212
}
213
}
214
215
peekAndMatch(",");
216
217
// May see the exemption mechanism name.
218
if (peek("\"")) {
219
// Exemption mechanism name - convert to upper case too.
220
e.exemptionMechanism = match("quoted string").toUpperCase(ENGLISH);
221
}
222
223
peekAndMatch(",");
224
225
// Check whether this entry is consistent with other permission entries
226
// that have been read.
227
if (!isConsistent(e.alg, e.exemptionMechanism, processedPermissions)) {
228
throw new ParsingException(st.lineno(), "Inconsistent policy");
229
}
230
231
// Should see the maxKeySize if not at the end of this entry yet.
232
if (peek("number")) {
233
e.maxKeySize = match();
234
} else {
235
if (peek("*")) {
236
match("*");
237
e.maxKeySize = Integer.MAX_VALUE;
238
} else {
239
if (!peek(";")) {
240
throw new ParsingException(st.lineno(),
241
"Missing the maximum " +
242
"allowable key size");
243
} else {
244
// At the end of this permission entry
245
e.maxKeySize = Integer.MAX_VALUE;
246
}
247
}
248
}
249
250
peekAndMatch(",");
251
252
// May see an AlgorithmParameterSpec class name.
253
if (peek("\"")) {
254
// AlgorithmParameterSpec class name.
255
String algParamSpecClassName = match("quoted string");
256
257
Vector<Integer> paramsV = new Vector<>(1);
258
while (peek(",")) {
259
match(",");
260
if (peek("number")) {
261
paramsV.addElement(match());
262
} else {
263
if (peek("*")) {
264
match("*");
265
paramsV.addElement(Integer.MAX_VALUE);
266
} else {
267
throw new ParsingException(st.lineno(),
268
"Expecting an integer");
269
}
270
}
271
}
272
273
Integer[] params = new Integer[paramsV.size()];
274
paramsV.copyInto(params);
275
276
e.checkParam = true;
277
e.algParamSpec = getInstance(algParamSpecClassName, params);
278
}
279
280
return e;
281
}
282
283
private static final AlgorithmParameterSpec getInstance(String type,
284
Integer[] params)
285
throws ParsingException
286
{
287
AlgorithmParameterSpec ret = null;
288
289
try {
290
Class<?> apsClass = Class.forName(type);
291
Class<?>[] paramClasses = new Class<?>[params.length];
292
293
for (int i = 0; i < params.length; i++) {
294
paramClasses[i] = int.class;
295
}
296
297
Constructor<?> c = apsClass.getConstructor(paramClasses);
298
ret = (AlgorithmParameterSpec) c.newInstance((Object[]) params);
299
} catch (Exception e) {
300
throw new ParsingException("Cannot call the constructor of " +
301
type + e);
302
}
303
return ret;
304
}
305
306
307
private boolean peekAndMatch(String expect)
308
throws ParsingException, IOException
309
{
310
if (peek(expect)) {
311
match(expect);
312
return true;
313
}
314
return false;
315
}
316
317
private boolean peek(String expect) {
318
boolean found = false;
319
320
switch (lookahead) {
321
322
case StreamTokenizer.TT_WORD:
323
if (expect.equalsIgnoreCase(st.sval))
324
found = true;
325
break;
326
case StreamTokenizer.TT_NUMBER:
327
if (expect.equalsIgnoreCase("number")) {
328
found = true;
329
}
330
break;
331
case ',':
332
if (expect.equals(","))
333
found = true;
334
break;
335
case '{':
336
if (expect.equals("{"))
337
found = true;
338
break;
339
case '}':
340
if (expect.equals("}"))
341
found = true;
342
break;
343
case '"':
344
if (expect.equals("\""))
345
found = true;
346
break;
347
case '*':
348
if (expect.equals("*"))
349
found = true;
350
break;
351
case ';':
352
if (expect.equals(";"))
353
found = true;
354
break;
355
default:
356
break;
357
}
358
return found;
359
}
360
361
/**
362
* Excepts to match a non-negative number.
363
*/
364
private int match()
365
throws ParsingException, IOException
366
{
367
int value = -1;
368
int lineno = st.lineno();
369
String sValue = null;
370
371
switch (lookahead) {
372
case StreamTokenizer.TT_NUMBER:
373
value = (int)st.nval;
374
if (value < 0) {
375
sValue = String.valueOf(st.nval);
376
}
377
lookahead = st.nextToken();
378
break;
379
default:
380
sValue = st.sval;
381
break;
382
}
383
if (value <= 0) {
384
throw new ParsingException(lineno, "a non-negative number",
385
sValue);
386
}
387
return value;
388
}
389
390
private String match(String expect)
391
throws ParsingException, IOException
392
{
393
String value = null;
394
395
switch (lookahead) {
396
case StreamTokenizer.TT_NUMBER:
397
throw new ParsingException(st.lineno(), expect,
398
"number "+String.valueOf(st.nval));
399
case StreamTokenizer.TT_EOF:
400
throw new ParsingException("expected "+expect+", read end of file");
401
case StreamTokenizer.TT_WORD:
402
if (expect.equalsIgnoreCase(st.sval)) {
403
lookahead = st.nextToken();
404
}
405
else if (expect.equalsIgnoreCase("permission type")) {
406
value = st.sval;
407
lookahead = st.nextToken();
408
}
409
else
410
throw new ParsingException(st.lineno(), expect, st.sval);
411
break;
412
case '"':
413
if (expect.equalsIgnoreCase("quoted string")) {
414
value = st.sval;
415
lookahead = st.nextToken();
416
} else if (expect.equalsIgnoreCase("permission type")) {
417
value = st.sval;
418
lookahead = st.nextToken();
419
}
420
else
421
throw new ParsingException(st.lineno(), expect, st.sval);
422
break;
423
case ',':
424
if (expect.equals(","))
425
lookahead = st.nextToken();
426
else
427
throw new ParsingException(st.lineno(), expect, ",");
428
break;
429
case '{':
430
if (expect.equals("{"))
431
lookahead = st.nextToken();
432
else
433
throw new ParsingException(st.lineno(), expect, "{");
434
break;
435
case '}':
436
if (expect.equals("}"))
437
lookahead = st.nextToken();
438
else
439
throw new ParsingException(st.lineno(), expect, "}");
440
break;
441
case ';':
442
if (expect.equals(";"))
443
lookahead = st.nextToken();
444
else
445
throw new ParsingException(st.lineno(), expect, ";");
446
break;
447
case '*':
448
if (expect.equals("*"))
449
lookahead = st.nextToken();
450
else
451
throw new ParsingException(st.lineno(), expect, "*");
452
break;
453
default:
454
throw new ParsingException(st.lineno(), expect,
455
String.valueOf((char)lookahead));
456
}
457
return value;
458
}
459
460
CryptoPermission[] getPermissions() {
461
Vector<CryptoPermission> result = new Vector<>();
462
463
Enumeration<GrantEntry> grantEnum = grantEntries.elements();
464
while (grantEnum.hasMoreElements()) {
465
GrantEntry ge = grantEnum.nextElement();
466
Enumeration<CryptoPermissionEntry> permEnum =
467
ge.permissionElements();
468
while (permEnum.hasMoreElements()) {
469
CryptoPermissionEntry pe = permEnum.nextElement();
470
if (pe.cryptoPermission.equals(
471
"javax.crypto.CryptoAllPermission")) {
472
result.addElement(CryptoAllPermission.INSTANCE);
473
} else {
474
if (pe.checkParam) {
475
result.addElement(new CryptoPermission(
476
pe.alg,
477
pe.maxKeySize,
478
pe.algParamSpec,
479
pe.exemptionMechanism));
480
} else {
481
result.addElement(new CryptoPermission(
482
pe.alg,
483
pe.maxKeySize,
484
pe.exemptionMechanism));
485
}
486
}
487
}
488
}
489
490
CryptoPermission[] ret = new CryptoPermission[result.size()];
491
result.copyInto(ret);
492
493
return ret;
494
}
495
496
private boolean isConsistent(String alg, String exemptionMechanism,
497
Hashtable<String, Vector<String>> processedPermissions) {
498
String thisExemptionMechanism =
499
exemptionMechanism == null ? "none" : exemptionMechanism;
500
501
if (processedPermissions == null) {
502
processedPermissions = new Hashtable<String, Vector<String>>();
503
Vector<String> exemptionMechanisms = new Vector<>(1);
504
exemptionMechanisms.addElement(thisExemptionMechanism);
505
processedPermissions.put(alg, exemptionMechanisms);
506
return true;
507
}
508
509
if (processedPermissions.containsKey(CryptoAllPermission.ALG_NAME)) {
510
return false;
511
}
512
513
Vector<String> exemptionMechanisms;
514
515
if (processedPermissions.containsKey(alg)) {
516
exemptionMechanisms = processedPermissions.get(alg);
517
if (exemptionMechanisms.contains(thisExemptionMechanism)) {
518
return false;
519
}
520
} else {
521
exemptionMechanisms = new Vector<String>(1);
522
}
523
524
exemptionMechanisms.addElement(thisExemptionMechanism);
525
processedPermissions.put(alg, exemptionMechanisms);
526
return true;
527
}
528
529
/**
530
* Each grant entry in the policy configuration file is represented by a
531
* GrantEntry object.
532
* <p>
533
* For example, the entry
534
* <pre>
535
* grant {
536
* permission javax.crypto.CryptoPermission "DES", 56;
537
* };
538
*
539
* </pre>
540
* is represented internally
541
* <pre>
542
*
543
* pe = new CryptoPermissionEntry("javax.crypto.CryptoPermission",
544
* "DES", 56);
545
*
546
* ge = new GrantEntry();
547
*
548
* ge.add(pe);
549
*
550
* </pre>
551
*
552
* @see java.security.Permission
553
* @see javax.crypto.CryptoPermission
554
* @see javax.crypto.CryptoPermissions
555
*/
556
557
private static class GrantEntry {
558
559
private Vector<CryptoPermissionEntry> permissionEntries;
560
561
GrantEntry() {
562
permissionEntries = new Vector<CryptoPermissionEntry>();
563
}
564
565
void add(CryptoPermissionEntry pe)
566
{
567
permissionEntries.addElement(pe);
568
}
569
570
boolean remove(CryptoPermissionEntry pe)
571
{
572
return permissionEntries.removeElement(pe);
573
}
574
575
boolean contains(CryptoPermissionEntry pe)
576
{
577
return permissionEntries.contains(pe);
578
}
579
580
/**
581
* Enumerate all the permission entries in this GrantEntry.
582
*/
583
Enumeration<CryptoPermissionEntry> permissionElements(){
584
return permissionEntries.elements();
585
}
586
587
}
588
589
/**
590
* Each crypto permission entry in the policy configuration file is
591
* represented by a CryptoPermissionEntry object.
592
* <p>
593
* For example, the entry
594
* <pre>
595
* permission javax.crypto.CryptoPermission "DES", 56;
596
* </pre>
597
* is represented internally
598
* <pre>
599
*
600
* pe = new CryptoPermissionEntry("javax.crypto.cryptoPermission",
601
* "DES", 56);
602
* </pre>
603
*
604
* @see java.security.Permissions
605
* @see javax.crypto.CryptoPermission
606
* @see javax.crypto.CryptoAllPermission
607
*/
608
609
private static class CryptoPermissionEntry {
610
611
String cryptoPermission;
612
String alg;
613
String exemptionMechanism;
614
int maxKeySize;
615
boolean checkParam;
616
AlgorithmParameterSpec algParamSpec;
617
618
CryptoPermissionEntry() {
619
// Set default values.
620
maxKeySize = 0;
621
alg = null;
622
exemptionMechanism = null;
623
checkParam = false;
624
algParamSpec = null;
625
}
626
627
/**
628
* Calculates a hash code value for the object. Objects
629
* which are equal will also have the same hashcode.
630
*/
631
public int hashCode() {
632
int retval = cryptoPermission.hashCode();
633
if (alg != null) retval ^= alg.hashCode();
634
if (exemptionMechanism != null) {
635
retval ^= exemptionMechanism.hashCode();
636
}
637
retval ^= maxKeySize;
638
if (checkParam) retval ^= 100;
639
if (algParamSpec != null) {
640
retval ^= algParamSpec.hashCode();
641
}
642
return retval;
643
}
644
645
public boolean equals(Object obj) {
646
if (obj == this)
647
return true;
648
649
if (!(obj instanceof CryptoPermissionEntry))
650
return false;
651
652
CryptoPermissionEntry that = (CryptoPermissionEntry) obj;
653
654
if (this.cryptoPermission == null) {
655
if (that.cryptoPermission != null) return false;
656
} else {
657
if (!this.cryptoPermission.equals(
658
that.cryptoPermission))
659
return false;
660
}
661
662
if (this.alg == null) {
663
if (that.alg != null) return false;
664
} else {
665
if (!this.alg.equalsIgnoreCase(that.alg))
666
return false;
667
}
668
669
if (!(this.maxKeySize == that.maxKeySize)) return false;
670
671
if (this.checkParam != that.checkParam) return false;
672
673
if (this.algParamSpec == null) {
674
if (that.algParamSpec != null) return false;
675
} else {
676
if (!this.algParamSpec.equals(that.algParamSpec))
677
return false;
678
}
679
680
// everything matched -- the 2 objects are equal
681
return true;
682
}
683
}
684
685
static final class ParsingException extends GeneralSecurityException {
686
687
@java.io.Serial
688
private static final long serialVersionUID = 7147241245566588374L;
689
690
/**
691
* Constructs a ParsingException with the specified
692
* detail message.
693
* @param msg the detail message.
694
*/
695
ParsingException(String msg) {
696
super(msg);
697
}
698
699
ParsingException(int line, String msg) {
700
super("line " + line + ": " + msg);
701
}
702
703
ParsingException(int line, String expect, String actual) {
704
super("line "+line+": expected '"+expect+"', found '"+actual+"'");
705
}
706
}
707
}
708
709