-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8346129: Simplify EdDSA & XDH curve name usage #23647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f147743
2b9a2f0
08da228
a56e198
42a329a
de2a57a
38c501e
d2a4f0b
9fbddcc
58aa6d6
203833b
3cbee6a
b26118a
a0fc101
799c2ac
6ff9f8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /* | ||
| * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. | ||
| * Copyright (c) 2010, 2025, 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 | ||
|
|
@@ -34,18 +34,15 @@ | |
| import java.security.cert.CertPathValidatorException; | ||
| import java.security.cert.CertPathValidatorException.BasicReason; | ||
| import java.security.interfaces.ECKey; | ||
| import java.security.interfaces.XECKey; | ||
| import java.security.spec.AlgorithmParameterSpec; | ||
| import java.security.spec.InvalidParameterSpecException; | ||
| import java.security.spec.MGF1ParameterSpec; | ||
| import java.security.spec.NamedParameterSpec; | ||
| import java.security.spec.PSSParameterSpec; | ||
| import java.time.DateTimeException; | ||
| import java.time.Instant; | ||
| import java.time.ZoneId; | ||
| import java.time.ZonedDateTime; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.Collection; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
|
|
@@ -254,7 +251,7 @@ public final void permits(String algorithm, ConstraintsParameters cp, | |
| if (checkKey) { | ||
| // Check if named curves in the key are disabled. | ||
| for (Key key : cp.getKeys()) { | ||
| for (String curve : getNamedCurveFromKey(key)) { | ||
| for (String curve : getNamedParametersFromKey(key)) { | ||
| if (!cachedCheckAlgorithm(curve)) { | ||
| throw new CertPathValidatorException( | ||
| "Algorithm constraints check failed on disabled " + | ||
|
|
@@ -267,17 +264,23 @@ public final void permits(String algorithm, ConstraintsParameters cp, | |
| algorithmConstraints.permits(algorithm, cp, checkKey); | ||
| } | ||
|
|
||
| private static List<String> getNamedCurveFromKey(Key key) { | ||
| if (key instanceof ECKey) { | ||
| NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams()); | ||
| return (nc == null ? List.of() | ||
| : Arrays.asList(nc.getNameAndAliases())); | ||
| } else if (key instanceof XECKey) { | ||
| return List.of( | ||
| ((NamedParameterSpec)((XECKey)key).getParams()).getName()); | ||
| } else { | ||
| return List.of(); | ||
| } | ||
| private static List<String> getNamedParametersFromKey(Key key) { | ||
| return switch (key) { | ||
| case ECKey ecKey -> { | ||
| NamedCurve nc = CurveDB.lookup(ecKey.getParams()); | ||
| if (nc == null) { | ||
| yield List.of(); | ||
| } | ||
| yield List.of(nc.getNameAndAliases()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to add
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, "EC" isn't an alias for a particular curve. |
||
| } | ||
| default -> { | ||
| String n = KeyUtil.getAlgorithm(key); | ||
| if (n.equalsIgnoreCase(key.getAlgorithm())) { | ||
| yield List.of(n); | ||
| } | ||
| yield List.of(key.getAlgorithm(), n); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| // Check algorithm constraints with key and algorithm | ||
|
|
@@ -301,12 +304,12 @@ private boolean checkConstraints(Set<CryptoPrimitive> primitives, | |
| } | ||
|
|
||
| // check the key algorithm | ||
| if (!permits(primitives, key.getAlgorithm(), null)) { | ||
| if (!permits(primitives, KeyUtil.getAlgorithm(key), null)) { | ||
| return false; | ||
| } | ||
|
|
||
| // If this is an elliptic curve, check if it is disabled | ||
| for (String curve : getNamedCurveFromKey(key)) { | ||
| for (String curve : getNamedParametersFromKey(key)) { | ||
| if (!permits(primitives, curve, null)) { | ||
| return false; | ||
| } | ||
|
|
@@ -460,7 +463,7 @@ private List<Constraint> getConstraints(String algorithm) { | |
|
|
||
| // Check if KeySizeConstraints permit the specified key | ||
| public boolean permits(Key key) { | ||
| List<Constraint> list = getConstraints(key.getAlgorithm()); | ||
| List<Constraint> list = getConstraints(KeyUtil.getAlgorithm(key)); | ||
| if (list == null) { | ||
| return true; | ||
| } | ||
|
|
@@ -514,7 +517,7 @@ public void permits(String algorithm, ConstraintsParameters cp, | |
|
|
||
| if (checkKey) { | ||
| for (Key key : cp.getKeys()) { | ||
| algorithms.add(key.getAlgorithm()); | ||
| algorithms.add(KeyUtil.getAlgorithm(key)); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,6 +174,22 @@ public static final int getKeySize(AlgorithmParameters parameters) { | |
| return -1; | ||
| } | ||
|
|
||
| /** | ||
| * If the key is a sub-algorithm of a larger group of algorithms, this | ||
| * method will return that sub-algorithm. For example, key.getAlgorithm() | ||
| * returns "EdDSA", but the underlying key may be "Ed448". For | ||
| * DisabledAlgorithmConstraints (DAC), this distinction is important. | ||
| * "EdDSA" means all curves for DAC, but when using it with | ||
| * KeyPairGenerator, "EdDSA" means "Ed25519". | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just say this allows more precise check for DAC. For
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see what your saying, but I was only explaining when EdDSA & Ed25519 can mean the same with KPG. As this is an internal method, I wasn't trying to explaining how to generate an Ed488 key.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. |
||
| */ | ||
| public static String getAlgorithm(Key key) { | ||
| if (key instanceof AsymmetricKey ak && | ||
| ak.getParams() instanceof NamedParameterSpec nps) { | ||
| return nps.getName(); | ||
| } | ||
| return key.getAlgorithm(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the key is valid or not. | ||
| * <P> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| /* | ||
| * Copyright (c) 2025, 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. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| import sun.security.util.DisabledAlgorithmConstraints; | ||
|
|
||
| import java.security.CryptoPrimitive; | ||
| import java.security.KeyPairGenerator; | ||
| import java.security.PrivateKey; | ||
| import java.security.Security; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.Set; | ||
|
|
||
| /* | ||
| * @test | ||
| * @bug 8346129 | ||
| * @modules java.base/sun.security.util | ||
| * @summary Check DisabledAlgorithmConstraints using EdDSA & XDH against | ||
| * permit(Set<CryptoPrimitive>, String, AP) and | ||
| * permit(Set<CryptoPrimitive>, Key). Results will differ based | ||
| * on method used. The first method only can compare the String with the | ||
| * algorithms. The second can check the algorithm and NamedParameterSpec | ||
| * while results in more 'false' cases. | ||
| * | ||
| * @run main/othervm DisabledAlgorithmPermits Ed25519 | ||
| * @run main/othervm DisabledAlgorithmPermits Ed448 | ||
| * @run main/othervm DisabledAlgorithmPermits EdDSA | ||
| * @run main/othervm DisabledAlgorithmPermits X25519 | ||
| * @run main/othervm DisabledAlgorithmPermits X448 | ||
| * @run main/othervm DisabledAlgorithmPermits XDH | ||
| */ | ||
|
|
||
| public class DisabledAlgorithmPermits { | ||
| public static void main(String[] args) throws Exception { | ||
| String algorithm = args[0]; | ||
| Security.setProperty("x", algorithm); | ||
|
|
||
| // Expected table are the expected results from the test | ||
| List<TestCase> expected = switch (algorithm) { | ||
| case "Ed25519" -> | ||
| Arrays.asList( | ||
| new TestCase("EdDSA", true), | ||
| new TestCase("Ed25519", false), | ||
| new TestCase("Ed448", true), | ||
| new TestCase("X448", true), | ||
| new TestCase(1,"EdDSA", false), | ||
| new TestCase(1,"Ed25519", false), | ||
| new TestCase(1,"Ed448", true), | ||
| new TestCase(1,"X448", true)); | ||
| case "Ed448" -> | ||
| Arrays.asList( | ||
| new TestCase("EdDSA", true), | ||
| new TestCase("Ed25519", true), | ||
| new TestCase("Ed448", false), | ||
| new TestCase("X448", true), | ||
| new TestCase(1,"EdDSA", true), | ||
| new TestCase(1,"Ed25519", true), | ||
| new TestCase(1,"Ed448", false), | ||
| new TestCase(1,"X448", true)); | ||
| case "EdDSA" -> | ||
| Arrays.asList( | ||
| new TestCase("EdDSA", false), | ||
| new TestCase("Ed25519", true), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why should the above pass? If you disable
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by this comment. With removing the hardcoded aliases in AbstractAlgorithmConstraints, which is what I thought you had suggested, EdDSA and Ed25519 are now separate as the check is effectively a string compare check against the disabledAlgorithm list The second half of that case statement has a key that can check against both EdDSA and the NPS. With respect to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I remember that. I understand there will be multiple checks in TLS and CertPath. Do we have existing tests on that level?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have other tests that check this. |
||
| new TestCase("Ed448", true), | ||
| new TestCase("X448", true), | ||
| new TestCase(1,"EdDSA", false), | ||
| new TestCase(1,"Ed25519", false), | ||
| new TestCase(1,"Ed448", false), | ||
| new TestCase(1,"X448", true)); | ||
| case "X25519" -> | ||
| Arrays.asList( | ||
| new TestCase("XDH", true), | ||
| new TestCase("X25519", false), | ||
| new TestCase("X448", true), | ||
| new TestCase("Ed448", true), | ||
| new TestCase(1, "XDH", false), | ||
| new TestCase(1, "X25519", false), | ||
| new TestCase(1, "X448", true), | ||
| new TestCase(1, "Ed448", true)); | ||
| case "X448" -> | ||
| Arrays.asList( | ||
| new TestCase("XDH", true), | ||
| new TestCase("X25519", true), | ||
| new TestCase("X448", false), | ||
| new TestCase("Ed448", true), | ||
| new TestCase(1, "XDH", true), | ||
| new TestCase(1, "X25519", true), | ||
| new TestCase(1, "X448", false), | ||
| new TestCase(1, "Ed448", true)); | ||
| case "XDH" -> | ||
| Arrays.asList( | ||
| new TestCase("XDH", false), | ||
| new TestCase("X25519", true), | ||
| new TestCase("X448", true), | ||
| new TestCase("Ed448", true), | ||
| new TestCase(1, "XDH", false), | ||
| new TestCase(1, "X25519", false), | ||
| new TestCase(1, "X448", false), | ||
| new TestCase(1, "Ed448", true)); | ||
| default -> null; | ||
| }; | ||
|
|
||
| Objects.requireNonNull(expected, "algorithm being tested " + | ||
| algorithm + " not in expected table"); | ||
| System.out.println("---"); | ||
| var dac = new DisabledAlgorithmConstraints("x"); | ||
| System.out.println("disabled algorithms = " + Security.getProperty("x")); | ||
|
|
||
| // Using only testType 0, this tests that permit(Set<>, String, null) | ||
| // will check only the algorithm against the disabled list | ||
| expected.stream().filter(n->n.testType == 0).forEach(tc -> { | ||
| boolean r = dac.permits(Set.of(CryptoPrimitive.SIGNATURE), | ||
| tc.testAlgo, null); | ||
| System.out.print("\tpermits(Set.of(CryptoPrimitive.SIGNATURE), \"" + | ||
| tc.testAlgo + "\", null): " + r + " : " ); | ||
| if (r != tc.expected) { | ||
| System.out.println("failed."); | ||
| throw new AssertionError("failed. Expected " + | ||
| tc.expected); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest replacing this check with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I did that, then I couldn't println to stdout. I'd rather keep it as is. |
||
| System.out.println("pass"); | ||
| }); | ||
|
|
||
| // Using only testType 1, this tests permit(Set<>, Key) that will look | ||
| // at both the key.getAlgorithm() and the key.getParams().getName() | ||
| // against the disabled list | ||
| expected.stream().filter(n->n.testType == 1).forEach(tc -> { | ||
| PrivateKey k; | ||
| try { | ||
| k = KeyPairGenerator.getInstance(tc.testAlgo).generateKeyPair(). | ||
| getPrivate(); | ||
| } catch (Exception e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| boolean r = dac.permits(Set.of(CryptoPrimitive.SIGNATURE), k); | ||
| System.out.print("\tpermits(Set.of(CryptoPrimitive.SIGNATURE), " + | ||
| tc.testAlgo + " privkey): " + r + " : " ); | ||
| if (r != tc.expected) { | ||
| System.out.println("failed."); | ||
| throw new AssertionError("failed. Expected " + | ||
| tc.expected); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. |
||
| System.out.println("pass"); | ||
| }); | ||
| System.out.println("---"); | ||
| } | ||
|
|
||
| record TestCase(int testType, String testAlgo, boolean expected) { | ||
| TestCase(String testAlgo, boolean expected) { | ||
| this(0, testAlgo, expected); | ||
| } | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you plan to have a unit test for
AlgorithmCheckerchanges? It looks like certificates usingED25519algorithm didn't match that check before. It would be useful to have a test where we disableED25519in java.security and then try to use a certificate withED25519algorithm.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is checked by an existing test