From 60f7616950c467c40322ea4dbd24d9ce421c67d8 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Fri, 14 Feb 2025 16:21:27 -0500 Subject: [PATCH 1/3] the code change --- .../sun/security/ec/ECPrivateKeyImpl.java | 13 +-- .../sun/security/ec/ECPublicKeyImpl.java | 8 +- .../classes/sun/security/util/ECUtil.java | 18 +++- .../sun/security/pkcs11/P11ECKeyFactory.java | 10 +- .../classes/sun/security/pkcs11/P11Key.java | 32 +++++-- .../security/pkcs11/P11KeyPairGenerator.java | 4 +- .../sun/security/pkcs11/P11KeyStore.java | 10 +- .../sun/security/pkcs11/P11KeyWrapCipher.java | 3 +- .../sun/security/pkcs11/KeyStore/Basic.java | 34 ++++++- .../pkcs11/ec/CalculatePublicKey.java | 96 +++++++++++++++++++ 10 files changed, 192 insertions(+), 36 deletions(-) create mode 100644 test/jdk/sun/security/pkcs11/ec/CalculatePublicKey.java diff --git a/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java index b1b8b2d188f2c..1ec3e21b8c197 100644 --- a/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -35,8 +35,6 @@ import java.security.spec.*; import java.util.Arrays; -import sun.security.ec.point.AffinePoint; -import sun.security.ec.point.MutablePoint; import sun.security.util.*; import sun.security.x509.AlgorithmId; import sun.security.pkcs.PKCS8Key; @@ -207,15 +205,8 @@ private void parseKeyBits() throws InvalidKeyException { @Override public PublicKey calculatePublicKey() { - ECParameterSpec ecParams = getParams(); - ECOperations ops = ECOperations.forParameters(ecParams) - .orElseThrow(ProviderException::new); - MutablePoint pub = ops.multiply(ecParams.getGenerator(), getArrayS0()); - AffinePoint affPub = pub.asAffine(); - ECPoint w = new ECPoint(affPub.getX().asBigInteger(), - affPub.getY().asBigInteger()); try { - return new ECPublicKeyImpl(w, ecParams); + return ECUtil.sArrayToPublicKey(getArrayS0(), getParams()); } catch (InvalidKeyException e) { throw new ProviderException( "Unexpected error calculating public key", e); diff --git a/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java index 29894038a1d49..b057f0a749106 100644 --- a/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -58,9 +58,9 @@ public final class ECPublicKeyImpl extends X509Key implements ECPublicKey { /** * Construct a key from its components. Used by the - * ECKeyFactory. + * ECKeyFactory and ECUtil. */ - ECPublicKeyImpl(ECPoint w, ECParameterSpec params) + public ECPublicKeyImpl(ECPoint w, ECParameterSpec params) throws InvalidKeyException { this.w = w; this.params = params; @@ -142,4 +142,4 @@ private void readObject(ObjectInputStream stream) throw new InvalidObjectException( "ECPublicKeyImpl keys are not directly deserializable"); } -} \ No newline at end of file +} diff --git a/src/java.base/share/classes/sun/security/util/ECUtil.java b/src/java.base/share/classes/sun/security/util/ECUtil.java index bcda2d7832cbe..24bd4a714c211 100644 --- a/src/java.base/share/classes/sun/security/util/ECUtil.java +++ b/src/java.base/share/classes/sun/security/util/ECUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -26,6 +26,10 @@ package sun.security.util; import jdk.internal.access.SharedSecrets; +import sun.security.ec.ECOperations; +import sun.security.ec.ECPublicKeyImpl; +import sun.security.ec.point.AffinePoint; +import sun.security.ec.point.MutablePoint; import java.io.IOException; import java.math.BigInteger; @@ -375,5 +379,17 @@ public static void validatePublicKey(ECPoint point, ECParameterSpec spec) } } + // Calculate an ECPublicKey from the private sArray component + public static ECPublicKey sArrayToPublicKey(byte[] sArray, ECParameterSpec params) + throws InvalidKeyException { + ECOperations ops = ECOperations.forParameters(params) + .orElseThrow(ProviderException::new); + MutablePoint pub = ops.multiply(params.getGenerator(), sArray); + AffinePoint affPub = pub.asAffine(); + ECPoint w = new ECPoint(affPub.getX().asBigInteger(), + affPub.getY().asBigInteger()); + return new ECPublicKeyImpl(w, params); + } + private ECUtil() {} } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java index 9896cb738bb32..874205adb3258 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -267,8 +267,14 @@ private PrivateKey generatePrivate(BigInteger s, ECParameterSpec params) try { session = token.getObjSession(); long keyID = token.p11.C_CreateObject(session.id(), attributes); + PublicKey pk = null; + try { + pk = ECUtil.sArrayToPublicKey(ECUtil.sArray(s, params), params); + } catch (Exception e) { + // Happens when params is not supported. Ignore it. + } return P11Key.privateKey - (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes); + (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes, pk); } finally { token.releaseSession(session); } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index bb2211ab9c16c..da316c7340c1e 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -49,6 +49,7 @@ import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import sun.security.util.DerValue; +import sun.security.util.InternalPrivateKey; import sun.security.util.Length; import sun.security.util.ECUtil; import sun.security.jca.JCAUtil; @@ -399,6 +400,13 @@ static PublicKey publicKey(Session session, long keyID, String algorithm, static PrivateKey privateKey(Session session, long keyID, String algorithm, int keyLength, CK_ATTRIBUTE[] attrs) { + return privateKey(session, keyID, algorithm, keyLength, attrs, null); + } + + // Create a PrivateKey with an optional PublicKey. The PublicKey is only + // added to EC keys at the moment. + static PrivateKey privateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attrs, PublicKey pk) { attrs = getAttributes(session, keyID, attrs, new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_TOKEN), new CK_ATTRIBUTE(CKA_SENSITIVE), @@ -417,7 +425,7 @@ static PrivateKey privateKey(Session session, long keyID, String algorithm, case "DH" -> P11DHPrivateKeyInternal.of(session, keyID, algorithm, keyLength, attrs, keySensitive); case "EC" -> P11ECPrivateKeyInternal.of(session, keyID, algorithm, - keyLength, attrs, keySensitive); + keyLength, attrs, keySensitive, pk); default -> throw new ProviderException ("Unknown private key algorithm " + algorithm); }; @@ -1201,28 +1209,31 @@ public boolean equals(Object obj) { } } - static class P11ECPrivateKeyInternal extends P11PrivateKey { + static class P11ECPrivateKeyInternal extends P11PrivateKey + implements InternalPrivateKey { @Serial private static final long serialVersionUID = 1L; + private final PublicKey pk; protected transient ECParameterSpec params; static P11ECPrivateKeyInternal of(Session session, long keyID, String algorithm, int keyLength, CK_ATTRIBUTE[] attrs, - boolean keySensitive) { + boolean keySensitive, PublicKey pk) { if (keySensitive) { return new P11ECPrivateKeyInternal(session, keyID, algorithm, - keyLength, attrs); + keyLength, attrs, pk); } else { return new P11ECPrivateKey(session, keyID, algorithm, - keyLength, attrs); + keyLength, attrs, pk); } } private P11ECPrivateKeyInternal(Session session, long keyID, - String algorithm, int keyLength, CK_ATTRIBUTE[] attrs) { + String algorithm, int keyLength, CK_ATTRIBUTE[] attrs, PublicKey pk) { super(session, keyID, algorithm, keyLength, attrs); + this.pk = pk; } private synchronized void fetchValues() { @@ -1245,6 +1256,11 @@ public ECParameterSpec getParams() { fetchValues(); return params; } + + @Override + public PublicKey calculatePublicKey() { + return pk; + } } private static final class P11ECPrivateKey extends P11ECPrivateKeyInternal @@ -1255,8 +1271,8 @@ private static final class P11ECPrivateKey extends P11ECPrivateKeyInternal private transient BigInteger s; // params in P11ECPrivateKeyInternal P11ECPrivateKey(Session session, long keyID, String algorithm, - int keyLength, CK_ATTRIBUTE[] attrs) { - super(session, keyID, algorithm, keyLength, attrs); + int keyLength, CK_ATTRIBUTE[] attrs, PublicKey pk) { + super(session, keyID, algorithm, keyLength, attrs, pk); } private synchronized void fetchValues() { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java index d32ebc81ba1ed..81771205a8b94 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -427,7 +427,7 @@ public KeyPair generateKeyPair() { PublicKey publicKey = P11Key.publicKey (session, keyIDs[0], algorithm, keySize, publicKeyTemplate); PrivateKey privateKey = P11Key.privateKey - (session, keyIDs[1], algorithm, keySize, privateKeyTemplate); + (session, keyIDs[1], algorithm, keySize, privateKeyTemplate, publicKey); return new KeyPair(publicKey, privateKey); } catch (PKCS11Exception e) { throw new ProviderException(e); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java index d1377b807fedf..d76562d1b61f0 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -313,7 +313,7 @@ public synchronized Key engineGetKey(String alias, char[] password) aliasInfo.id, null); if (h.type == ATTR_CLASS_PKEY) { - return loadPkey(session, h.handle); + return loadPkey(session, h.handle, aliasInfo.cert.getPublicKey()); } } else { THandle h = getTokenObject(session, @@ -968,8 +968,8 @@ public synchronized KeyStore.Entry engineGetEntry(String alias, throw new KeyStoreException ("expected but could not find private key"); } else { - PrivateKey pkey = loadPkey(session, h.handle); Certificate[] chain = aliasInfo.chain; + PrivateKey pkey = loadPkey(session, h.handle, chain[0].getPublicKey()); if ((pkey != null) && (chain != null)) { return new KeyStore.PrivateKeyEntry(pkey, chain); } else { @@ -1305,7 +1305,7 @@ private SecretKey loadSkey(Session session, long oHandle) return P11Key.secretKey(session, oHandle, keyType, keyLength, null); } - private PrivateKey loadPkey(Session session, long oHandle) + private PrivateKey loadPkey(Session session, long oHandle, PublicKey pk) throws PKCS11Exception, KeyStoreException { CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { @@ -1385,7 +1385,7 @@ private PrivateKey loadPkey(Session session, long oHandle) throw new KeyStoreException("Unsupported parameters", e); } - return P11Key.privateKey(session, oHandle, "EC", keyLength, null); + return P11Key.privateKey(session, oHandle, "EC", keyLength, null, pk); } else { if (debug != null) { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyWrapCipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyWrapCipher.java index 75b28d3eaabcf..d817cc4f1080d 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyWrapCipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyWrapCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -795,6 +795,7 @@ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgo, return (switch(wrappedKeyType) { case Cipher.PRIVATE_KEY -> P11Key.privateKey (session, unwrappedKeyID, wrappedKeyAlgo, -1, attributes); + // in this case, there is no way to get the associated public key case Cipher.SECRET_KEY -> P11Key.secretKey (session, unwrappedKeyID, wrappedKeyAlgo, -1, attributes); default -> null; diff --git a/test/jdk/sun/security/pkcs11/KeyStore/Basic.java b/test/jdk/sun/security/pkcs11/KeyStore/Basic.java index e03eff07c9536..b3da9cabb0867 100644 --- a/test/jdk/sun/security/pkcs11/KeyStore/Basic.java +++ b/test/jdk/sun/security/pkcs11/KeyStore/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -22,7 +22,7 @@ */ /* @test - * @bug 4938185 + * @bug 4938185 8350134 * @summary KeyStore support for NSS cert/key databases * To run manually: * set environment variable: @@ -33,6 +33,10 @@ * . 'list' lists the token aliases * . 'basic' does not run with activcard, * @library /test/lib .. + * @modules jdk.crypto.cryptoki + * java.base/sun.security.tools.keytool + * java.base/sun.security.util + * java.base/sun.security.x509 * @run testng/othervm Basic */ @@ -62,6 +66,9 @@ import org.testng.SkipException; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import sun.security.tools.keytool.CertAndKeyGen; +import sun.security.util.InternalPrivateKey; +import sun.security.x509.X500Name; public class Basic extends PKCS11Test { @@ -239,6 +246,7 @@ private static void doTest() throws Exception { testnum = Basic.pkey(testnum); testnum = Basic.sign(testnum); testnum = Basic.copy(testnum); + testnum = Basic.calculatePublicKey(testnum); } else if ("solaris".equals(token)) { testnum = Basic.setAttribute(testnum); testnum = Basic.pkey(testnum); @@ -913,6 +921,28 @@ private static int pkey(int testnum) throws Exception { return testnum; } + private static int calculatePublicKey(int testnum) throws Exception { + System.out.println("check calculatePublicKey in keystore"); + var kag = new CertAndKeyGen("EC", "SHA256withECDSA"); + kag.generate(-1); + + ks.setKeyEntry("a2", kag.getPrivateKey(), null, + new Certificate[] {kag.getSelfCertificate(new X500Name("CN=Me"), 1000)}); + var sk = ks.getKey("a2", null); + if (sk instanceof InternalPrivateKey ipk) { + var pk = ipk.calculatePublicKey(); + var cert = ks.getCertificate("a2"); + if (!cert.getPublicKey().equals(pk)) { + throw new SecurityException("Associated public key is not correct"); + } + } else { + throw new SecurityException("Not an InternalPrivateKey"); + } + System.out.println("test " + testnum++ + " passed"); + + return testnum; + } + private static int extended(int testnum) throws Exception { // setEntry unknown entry type diff --git a/test/jdk/sun/security/pkcs11/ec/CalculatePublicKey.java b/test/jdk/sun/security/pkcs11/ec/CalculatePublicKey.java new file mode 100644 index 0000000000000..73710ca5336ec --- /dev/null +++ b/test/jdk/sun/security/pkcs11/ec/CalculatePublicKey.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8350134 + * @summary Verify that pkcs11 EC keys has public key associated + * @library /test/lib .. + * @modules jdk.crypto.cryptoki + * java.base/sun.security.util + * @run main/othervm CalculatePublicKey p11-nss.txt + * @run main/othervm CalculatePublicKey p11-nss-sensitive.txt + */ + +import sun.security.util.InternalPrivateKey; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import java.io.IOException; +import java.nio.file.Path; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; + +public class CalculatePublicKey extends PKCS11Test { + + public static void main(String[] args) throws Exception { + main(new CalculatePublicKey(args[0]), args); + } + + public CalculatePublicKey(String conf) throws IOException { + copyNssCertKeyToClassesDir(); + setCommonSystemProps(); + System.setProperty("CUSTOM_P11_CONFIG", + Path.of(System.getProperty("test.src", "."), "../nss/" + conf).toString()); + System.setProperty("TOKEN", "nss"); + System.setProperty("TEST", "basic"); + } + + @Override + public void main(Provider p) throws Exception { + + // Newly generated + var kp = KeyPairGenerator.getInstance("EC", p).generateKeyPair(); + check(kp.getPrivate()); + + // Translate from another key + var kp2 = KeyPairGenerator.getInstance("EC", "SunEC").generateKeyPair(); + check((PrivateKey) KeyFactory.getInstance("EC", p).translateKey(kp2.getPrivate())); + + // Generate from PKCS8 + check(KeyFactory.getInstance("EC", p).generatePrivate( + new PKCS8EncodedKeySpec(kp2.getPrivate().getEncoded()))); + + // Unwrapped: not supported yet. + KeyGenerator kg = KeyGenerator.getInstance("AES", p); + kg.init(256); + var k = kg.generateKey(); + var cipher = Cipher.getInstance("AES_256/KW/PKCS5Padding", p); + cipher.init(Cipher.WRAP_MODE, k); + var wrapped = cipher.wrap(kp.getPrivate()); + cipher.init(Cipher.UNWRAP_MODE, k); + + // check((PrivateKey) cipher.unwrap(wrapped, "EC", Cipher.PRIVATE_KEY)); + } + + static void check(PrivateKey sk) throws Exception { + System.out.println(sk); + if (sk instanceof InternalPrivateKey ipk) { + if (ipk.calculatePublicKey() == null) { + throw new Exception("Associated public key is null"); + } + } else { + throw new Exception("Not an InternalPrivateKey"); + } + } +} From e97b172f9652e5518c645841064ecd426f673915 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Fri, 4 Apr 2025 14:24:38 -0400 Subject: [PATCH 2/3] add key slicing support --- .../com/sun/crypto/provider/DHKEM.java | 252 +++++++++++++----- .../sun/security/util/SliceableSecretKey.java | 51 ++++ .../classes/sun/security/pkcs11/P11Key.java | 41 ++- .../wrapper/CK_KEY_EXTRACT_FROM_KEY.java | 9 + .../security/pkcs11/wrapper/CK_MECHANISM.java | 4 + .../share/native/libj2pkcs11/p11_convert.c | 38 +++ .../share/native/libj2pkcs11/p11_util.c | 1 + .../share/native/libj2pkcs11/pkcs11wrapper.h | 2 +- .../sun/crypto/provider/DHKEM/Compliance.java | 138 ++++------ .../pkcs11/SecretKeyFactory/TestSlice.java | 92 +++++++ test/jdk/sun/security/pkcs11/ec/DHKEM.java | 114 ++++++++ 11 files changed, 585 insertions(+), 157 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/util/SliceableSecretKey.java create mode 100644 src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_KEY_EXTRACT_FROM_KEY.java create mode 100644 test/jdk/sun/security/pkcs11/SecretKeyFactory/TestSlice.java create mode 100644 test/jdk/sun/security/pkcs11/ec/DHKEM.java diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java index 3fa6b14762ac2..1c08ede5b68f7 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -26,24 +26,51 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.Serial; import java.math.BigInteger; -import java.security.*; -import java.security.interfaces.ECKey; +import java.security.AsymmetricKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; import java.security.interfaces.ECPublicKey; -import java.security.interfaces.XECKey; import java.security.interfaces.XECPublicKey; -import java.security.spec.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.XECPrivateKeySpec; +import java.security.spec.XECPublicKeySpec; import java.util.Arrays; import java.util.Objects; -import javax.crypto.*; +import javax.crypto.DecapsulateException; +import javax.crypto.KDF; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import javax.crypto.spec.HKDFParameterSpec; import javax.crypto.spec.SecretKeySpec; import sun.security.jca.JCAUtil; -import sun.security.ssl.HKDF; -import sun.security.util.*; +import sun.security.util.ArrayUtil; +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; +import sun.security.util.InternalPrivateKey; +import sun.security.util.NamedCurve; +import sun.security.util.SliceableSecretKey; // Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html, -// without the AuthEncap and AuthDecap functions public class DHKEM implements KEMSpi { private static final byte[] KEM = new byte[] @@ -63,7 +90,8 @@ public class DHKEM implements KEMSpi { private static final byte[] EMPTY = new byte[0]; private record Handler(Params params, SecureRandom secureRandom, - PrivateKey skR, PublicKey pkR) + PrivateKey skS, PublicKey pkS, // sender keys + PrivateKey skR, PublicKey pkR) // receiver keys implements EncapsulatorSpi, DecapsulatorSpi { @Override @@ -75,13 +103,21 @@ public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { PublicKey pkE = kpE.getPublic(); byte[] pkEm = params.SerializePublicKey(pkE); byte[] pkRm = params.SerializePublicKey(pkR); - byte[] kem_context = concat(pkEm, pkRm); try { - byte[] dh = params.DH(skE, pkR); - byte[] key = params.ExtractAndExpand(dh, kem_context); - return new KEM.Encapsulated( - new SecretKeySpec(key, from, to - from, algorithm), - pkEm, null); + SecretKey key; + if (skS == null) { + byte[] kem_context = concat(pkEm, pkRm); + key = params.deriveKey(algorithm, from, to, kem_context, + params.DH(skE, pkR)); + } else { + byte[] pkSm = params.SerializePublicKey(pkS); + byte[] kem_context = concat(pkEm, pkRm, pkSm); + key = params.deriveKey(algorithm, from, to, kem_context, + params.DH(skE, pkR), params.DH(skS, pkR)); + } + return new KEM.Encapsulated(key, pkEm, null); + } catch (UnsupportedOperationException e) { + throw e; } catch (Exception e) { throw new ProviderException("internal error", e); } @@ -98,11 +134,19 @@ public SecretKey engineDecapsulate(byte[] encapsulation, } try { PublicKey pkE = params.DeserializePublicKey(encapsulation); - byte[] dh = params.DH(skR, pkE); byte[] pkRm = params.SerializePublicKey(pkR); - byte[] kem_context = concat(encapsulation, pkRm); - byte[] key = params.ExtractAndExpand(dh, kem_context); - return new SecretKeySpec(key, from, to - from, algorithm); + if (pkS == null) { + byte[] kem_context = concat(encapsulation, pkRm); + return params.deriveKey(algorithm, from, to, kem_context, + params.DH(skR, pkE)); + } else { + byte[] pkSm = params.SerializePublicKey(pkS); + byte[] kem_context = concat(encapsulation, pkRm, pkSm); + return params.deriveKey(algorithm, from, to, kem_context, + params.DH(skR, pkE), params.DH(skR, pkS)); + } + } catch (UnsupportedOperationException e) { + throw e; } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { @@ -124,7 +168,8 @@ public int engineEncapsulationSize() { // Not really a random. For KAT test only. It generates key pair from ikm. public static class RFC9180DeriveKeyPairSR extends SecureRandom { - static final long serialVersionUID = 0L; + @Serial + private static final long serialVersionUID = 0L; private final byte[] ikm; @@ -133,7 +178,7 @@ public RFC9180DeriveKeyPairSR(byte[] ikm) { this.ikm = ikm; } - public KeyPair derive(Params params) { + private KeyPair derive(Params params) { try { return params.deriveKeyPair(ikm); } catch (Exception e) { @@ -153,19 +198,19 @@ public KeyPair derive(int kem_id) { private enum Params { P256(0x10, 32, 32, 2 * 32 + 1, - "ECDH", "EC", CurveDB.P_256, "SHA-256"), + "ECDH", "EC", CurveDB.P_256, "HKDF-SHA256"), P384(0x11, 48, 48, 2 * 48 + 1, - "ECDH", "EC", CurveDB.P_384, "SHA-384"), + "ECDH", "EC", CurveDB.P_384, "HKDF-SHA384"), P521(0x12, 64, 66, 2 * 66 + 1, - "ECDH", "EC", CurveDB.P_521, "SHA-512"), + "ECDH", "EC", CurveDB.P_521, "HKDF-SHA512"), X25519(0x20, 32, 32, 32, - "XDH", "XDH", NamedParameterSpec.X25519, "SHA-256"), + "XDH", "XDH", NamedParameterSpec.X25519, "HKDF-SHA256"), X448(0x21, 64, 56, 56, - "XDH", "XDH", NamedParameterSpec.X448, "SHA-512"), + "XDH", "XDH", NamedParameterSpec.X448, "HKDF-SHA512"), ; private final int kem_id; @@ -237,20 +282,53 @@ private PublicKey DeserializePublicKey(byte[] data) return KeyFactory.getInstance(keyAlgorithm).generatePublic(keySpec); } - private byte[] DH(PrivateKey skE, PublicKey pkR) + private SecretKey DH(PrivateKey skE, PublicKey pkR) throws NoSuchAlgorithmException, InvalidKeyException { KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); ka.init(skE); ka.doPhase(pkR, true); - return ka.generateSecret(); + return ka.generateSecret("Generic"); } - private byte[] ExtractAndExpand(byte[] dh, byte[] kem_context) - throws NoSuchAlgorithmException, InvalidKeyException { - HKDF kdf = new HKDF(hkdfAlgorithm); - SecretKey eae_prk = LabeledExtract(kdf, suiteId, null, EAE_PRK, dh); - return LabeledExpand(kdf, suiteId, eae_prk, SHARED_SECRET, - kem_context, Nsecret); + // The final shared secret derivation of either the encapsulator + // or the decapsulator. The key slicing is implemented inside. + // Throws UOE if a slice of the key cannot be found. + private SecretKey deriveKey(String alg, int from, int to, + byte[] kem_context, SecretKey... dhs) + throws NoSuchAlgorithmException { + if (from == 0 && to == Nsecret) { + return ExtractAndExpand(kem_context, alg, dhs); + } else { + // First get shared secrets in "Generic" and then get a slice + // of it in the requested algorithm. + var fullKey = ExtractAndExpand(kem_context, "Generic", dhs); + if ("RAW".equalsIgnoreCase(fullKey.getFormat())) { + byte[] km = fullKey.getEncoded(); + if (km == null) { + // Should not happen if format is "RAW" + throw new UnsupportedOperationException("Key extract failed"); + } else { + return new SecretKeySpec(km, from, to - from, alg); + } + } else if (fullKey instanceof SliceableSecretKey ssk) { + return ssk.slice(alg, from, to); + } else { + throw new UnsupportedOperationException("Cannot extract key"); + } + } + } + + private SecretKey ExtractAndExpand(byte[] kem_context, String alg, SecretKey... dhs) + throws NoSuchAlgorithmException { + var kdf = KDF.getInstance(hkdfAlgorithm); + var builder = labeledExtract(suiteId, EAE_PRK); + for (var dh : dhs) builder.addIKM(dh); + try { + return kdf.deriveKey(alg, + labeledExpand(builder, suiteId, SHARED_SECRET, kem_context, Nsecret)); + } catch (InvalidAlgorithmParameterException e) { + throw new ProviderException(e); + } } private PublicKey getPublicKey(PrivateKey sk) @@ -277,18 +355,19 @@ private PublicKey getPublicKey(PrivateKey sk) // For KAT tests only. See RFC9180DeriveKeyPairSR. public KeyPair deriveKeyPair(byte[] ikm) throws Exception { - HKDF kdf = new HKDF(hkdfAlgorithm); - SecretKey dkp_prk = LabeledExtract(kdf, suiteId, null, DKP_PRK, ikm); + var kdf = KDF.getInstance(hkdfAlgorithm); + var builder = labeledExtract(suiteId, DKP_PRK).addIKM(ikm); if (isEC()) { NamedCurve curve = (NamedCurve) spec; BigInteger sk = BigInteger.ZERO; int counter = 0; while (sk.signum() == 0 || sk.compareTo(curve.getOrder()) >= 0) { if (counter > 255) { - throw new RuntimeException(); + // So unlucky and should not happen + throw new ProviderException("DeriveKeyPairError"); } - byte[] bytes = LabeledExpand(kdf, suiteId, dkp_prk, - CANDIDATE, I2OSP(counter, 1), Nsk); + byte[] bytes = kdf.deriveData(labeledExpand(builder, + suiteId, CANDIDATE, I2OSP(counter, 1), Nsk)); // bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521 if (this == Params.P521) { bytes[0] = (byte) (bytes[0] & 0x01); @@ -299,7 +378,8 @@ public KeyPair deriveKeyPair(byte[] ikm) throws Exception { PrivateKey k = DeserializePrivateKey(sk.toByteArray()); return new KeyPair(getPublicKey(k), k); } else { - byte[] sk = LabeledExpand(kdf, suiteId, dkp_prk, SK, EMPTY, Nsk); + byte[] sk = kdf.deriveData(labeledExpand(builder, + suiteId, SK, EMPTY, Nsk)); PrivateKey k = DeserializePrivateKey(sk); return new KeyPair(getPublicKey(k), k); } @@ -328,7 +408,22 @@ public EncapsulatorSpi engineNewEncapsulator( throw new InvalidAlgorithmParameterException("no spec needed"); } Params params = paramsFromKey(pk); - return new Handler(params, getSecureRandom(secureRandom), null, pk); + return new Handler(params, getSecureRandom(secureRandom), null, null, null, pk); + } + + // AuthEncap is not public KEM API + public EncapsulatorSpi engineNewAuthEncapsulator(PublicKey pkR, PrivateKey skS, + AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (pkR == null || skS == null) { + throw new InvalidKeyException("input key is null"); + } + if (spec != null) { + throw new InvalidAlgorithmParameterException("no spec needed"); + } + Params params = paramsFromKey(pkR); + return new Handler(params, getSecureRandom(secureRandom), + skS, params.getPublicKey(skS), null, pkR); } @Override @@ -341,20 +436,34 @@ public DecapsulatorSpi engineNewDecapsulator(PrivateKey sk, AlgorithmParameterSp throw new InvalidAlgorithmParameterException("no spec needed"); } Params params = paramsFromKey(sk); - return new Handler(params, null, sk, params.getPublicKey(sk)); + return new Handler(params, null, null, null, sk, params.getPublicKey(sk)); } - private Params paramsFromKey(Key k) throws InvalidKeyException { - if (k instanceof ECKey eckey) { - if (ECUtil.equals(eckey.getParams(), CurveDB.P_256)) { + // AuthDecap is not public KEM API + public DecapsulatorSpi engineNewAuthDecapsulator( + PrivateKey skR, PublicKey pkS, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (skR == null || pkS == null) { + throw new InvalidKeyException("input key is null"); + } + if (spec != null) { + throw new InvalidAlgorithmParameterException("no spec needed"); + } + Params params = paramsFromKey(skR); + return new Handler(params, null, null, pkS, skR, params.getPublicKey(skR)); + } + + private Params paramsFromKey(AsymmetricKey k) throws InvalidKeyException { + var p = k.getParams(); + if (p instanceof ECParameterSpec ecp) { + if (ECUtil.equals(ecp, CurveDB.P_256)) { return Params.P256; - } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_384)) { + } else if (ECUtil.equals(ecp, CurveDB.P_384)) { return Params.P384; - } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_521)) { + } else if (ECUtil.equals(ecp, CurveDB.P_521)) { return Params.P521; } - } else if (k instanceof XECKey xkey - && xkey.getParams() instanceof NamedParameterSpec ns) { + } else if (p instanceof NamedParameterSpec ns) { if (ns.getName().equalsIgnoreCase("X25519")) { return Params.X25519; } else if (ns.getName().equalsIgnoreCase("X448")) { @@ -370,8 +479,11 @@ private static byte[] concat(byte[]... inputs) { return o.toByteArray(); } - private static byte[] I2OSP(int n, int w) { - assert n < 256; + // I2OSP(n, w) as defined in RFC 9180 Section 3. + // In DHKEM and HPKE, number is always <65536 + // and converted to at most 2 bytes. + public static byte[] I2OSP(int n, int w) { + assert n < 65536; assert w == 1 || w == 2; if (w == 1) { return new byte[] { (byte) n }; @@ -380,18 +492,32 @@ private static byte[] I2OSP(int n, int w) { } } - private static SecretKey LabeledExtract(HKDF kdf, byte[] suite_id, - byte[] salt, byte[] label, byte[] ikm) throws InvalidKeyException { - return kdf.extract(salt, - new SecretKeySpec(concat(HPKE_V1, suite_id, label, ikm), "IKM"), - "HKDF-PRK"); + // Create a LabeledExtract builder with labels. + // You can add more IKM and salt into the result. + public static HKDFParameterSpec.Builder labeledExtract( + byte[] suiteId, byte[] label) { + return HKDFParameterSpec.ofExtract() + .addIKM(HPKE_V1).addIKM(suiteId).addIKM(label); + } + + // Create a labeled info from info and labels + private static byte[] labeledInfo( + byte[] suiteId, byte[] label, byte[] info, int L) { + return concat(I2OSP(L, 2), HPKE_V1, suiteId, label, info); + } + + // LabeledExpand from a builder + public static HKDFParameterSpec labeledExpand( + HKDFParameterSpec.Builder builder, + byte[] suiteId, byte[] label, byte[] info, int L) { + return builder.thenExpand( + labeledInfo(suiteId, label, info, L), L); } - private static byte[] LabeledExpand(HKDF kdf, byte[] suite_id, - SecretKey prk, byte[] label, byte[] info, int L) - throws InvalidKeyException { - byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1, - suite_id, label, info); - return kdf.expand(prk, labeled_info, L, "NONE").getEncoded(); + // LabeledExpand from a prk + public static HKDFParameterSpec labeledExpand( + SecretKey prk, byte[] suiteId, byte[] label, byte[] info, int L) { + return HKDFParameterSpec.expandOnly( + prk, labeledInfo(suiteId, label, info, L), L); } } diff --git a/src/java.base/share/classes/sun/security/util/SliceableSecretKey.java b/src/java.base/share/classes/sun/security/util/SliceableSecretKey.java new file mode 100644 index 0000000000000..0de16be1a2b7c --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/SliceableSecretKey.java @@ -0,0 +1,51 @@ +/* + * 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. 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.util; + +import javax.crypto.SecretKey; + +/** + * An interface for SecretKeys that support using its slice as a new + * SecretKey + *

+ * This is mainly used by PKCS #11 implementations that support the + * EXTRACT_KEY_FROM_KEY mechanism even if the key itself is sensitive + * and non-extractable. + */ +public interface SliceableSecretKey { + + /** + * Returns a slice as a new SecretKey. + * + * @param alg the new algorithm name + * @param from the byte offset of the new key in the full key + * @param to the to offset (exclusive) of the new key in the full key + * @return the new key + * @throws ArrayIndexOutOfBoundsException for improper from + * and to values + * @throws UnsupportedOperationException if slicing is not supported + */ + SecretKey slice(String alg, int from, int to); +} diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index da316c7340c1e..b5a9c45e51385 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -53,6 +53,7 @@ import sun.security.util.Length; import sun.security.util.ECUtil; import sun.security.jca.JCAUtil; +import sun.security.util.SliceableSecretKey; /** * Key implementation classes. @@ -454,7 +455,7 @@ byte[] getEncodedInternal() { } } - static class P11SecretKey extends P11Key implements SecretKey { + static class P11SecretKey extends P11Key implements SecretKey, SliceableSecretKey { @Serial private static final long serialVersionUID = -7828241727014329084L; @@ -494,6 +495,42 @@ byte[] getEncodedInternal() { } return b; } + + @Override + public P11SecretKey slice(String alg, int from, int to) { + Objects.checkFromToIndex(from, to, length() / 8); + try { + CK_MECHANISM mechanism = new CK_MECHANISM(CKM_EXTRACT_KEY_FROM_KEY, + new CK_KEY_EXTRACT_FROM_KEY(from * 8)); + + P11SecretKeyFactory.KeyInfo ki = P11SecretKeyFactory.getKeyInfo(alg); + if (ki == null) { + throw new UnsupportedOperationException("A PKCS #11 key " + + "type (CKK_*) was not found for a key of the algorithm '" + + alg + "'."); + } + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, ki.keyType), + new CK_ATTRIBUTE(CKA_VALUE_LEN, to - from), + }; + + var session = token.getOpSession(); + attrs = token.getAttributes(O_GENERATE, CKO_SECRET_KEY, + ki.keyType, attrs); + long newKeyHandle = token.p11.C_DeriveKey( + session.id(), + mechanism, + getKeyID(), + attrs + ); + + return (P11Key.P11SecretKey) P11Key.secretKey(session, + newKeyHandle, alg, (to - from) * 8, null); + } catch (PKCS11Exception e) { + throw new UnsupportedOperationException(e); + } + } } // base class for all PKCS11 public keys @@ -1210,7 +1247,7 @@ public boolean equals(Object obj) { } static class P11ECPrivateKeyInternal extends P11PrivateKey - implements InternalPrivateKey { + implements ECKey, InternalPrivateKey { @Serial private static final long serialVersionUID = 1L; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_KEY_EXTRACT_FROM_KEY.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_KEY_EXTRACT_FROM_KEY.java new file mode 100644 index 0000000000000..0a12d3d8b9b09 --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_KEY_EXTRACT_FROM_KEY.java @@ -0,0 +1,9 @@ +package sun.security.pkcs11.wrapper; + +public class CK_KEY_EXTRACT_FROM_KEY { + private final int fromBit; + + public CK_KEY_EXTRACT_FROM_KEY(int fromBit) { + this.fromBit = fromBit; + } +} diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java index 4d518b7a167ae..e760c0747994e 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java @@ -135,6 +135,10 @@ public CK_MECHANISM(long mechanism, CK_KEY_DERIVATION_STRING_DATA params) { init(mechanism, params); } + public CK_MECHANISM(long mechanism, CK_KEY_EXTRACT_FROM_KEY params) { + init(mechanism, params); + } + public CK_MECHANISM(long mechanism, CK_ECDH1_DERIVE_PARAMS params) { init(mechanism, params); } diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c index f88477681b71c..2a269980a5333 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c @@ -875,6 +875,40 @@ jKeyDerivationStringDataToCKKeyDerivationStringDataPtr(JNIEnv *env, jobject jPar return NULL; } +CK_EXTRACT_PARAMS_PTR +jKeyExtractKeyFromKeyToExtractKeyFromKeyPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) +{ + CK_EXTRACT_PARAMS_PTR ckParamPtr = NULL; + jclass jKeyDerivationStringDataClass; + jfieldID fieldID; + jint jpData; + + if (pLength != NULL) { + *pLength = 0L; + } + + jKeyDerivationStringDataClass = (*env)->FindClass(env, CLASS_KEY_EXTRACT_FROM_KEY); + if (jKeyDerivationStringDataClass == NULL) { + return NULL; + } + fieldID = (*env)->GetFieldID(env, jKeyDerivationStringDataClass, "fromBit", "I"); + if (fieldID == NULL) { + return NULL; + } + jpData = (*env)->GetIntField(env, jParam, fieldID); + ckParamPtr = calloc(1, sizeof(CK_EXTRACT_PARAMS)); + if (ckParamPtr == NULL) { + p11ThrowOutOfMemoryError(env, 0); + return NULL; + } + *ckParamPtr = jpData; + + if (pLength != NULL) { + *pLength = sizeof(CK_EXTRACT_PARAMS); + } + return ckParamPtr; +} + void keyMatParamToCKKeyMatParam(JNIEnv *env, jobject jParam, jclass jKeyMatParamClass, CK_ULONG* cKKeyMatParamUlMacSizeInBits, @@ -1650,6 +1684,10 @@ CK_VOID_PTR jMechParamToCKMechParamPtrSlow(JNIEnv *env, jobject jParam, ckpParamPtr = jKeyDerivationStringDataToCKKeyDerivationStringDataPtr(env, jParam, ckpLength); break; + case CKM_EXTRACT_KEY_FROM_KEY: + ckpParamPtr = jKeyExtractKeyFromKeyToExtractKeyFromKeyPtr(env, jParam, + ckpLength); + break; case CKM_AES_CTR: ckpParamPtr = jAesCtrParamsToCKAesCtrParamPtr(env, jParam, ckpLength); diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c index 9c854e09141b4..2bde64ad0f5c4 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c @@ -416,6 +416,7 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) { case CKM_AES_CTR: case CKM_RSA_PKCS_PSS: case CKM_CAMELLIA_CTR: + case CKM_EXTRACT_KEY_FROM_KEY: // params do not contain pointers break; case CKM_PKCS5_PBKD2: diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h index 347ef00c93a15..0c341991fd61b 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h @@ -220,7 +220,6 @@ //#define TRACE2d(s, p1, p2) { printf(s, p1, p2); fflush(stdout); } //#define TRACE3d(s, p1, p2, p3) { printf(s, p1, p2, p3); fflush(stdout); } //#define TRACE4d(s, p1, p2, p3, p4) { printf(s, p1, p2, p3, p4); fflush(stdout); } - #ifdef P11_DEBUG #define TRACE0(s) { printf(s); fflush(stdout); } #define TRACE1(s, p1) { printf(s, p1); fflush(stdout); } @@ -300,6 +299,7 @@ void printDebug(const char *format, ...); */ #define CLASS_KEY_DERIVATION_STRING_DATA "sun/security/pkcs11/wrapper/CK_KEY_DERIVATION_STRING_DATA" +#define CLASS_KEY_EXTRACT_FROM_KEY "sun/security/pkcs11/wrapper/CK_KEY_EXTRACT_FROM_KEY" #define CLASS_SSL3_RANDOM_DATA "sun/security/pkcs11/wrapper/CK_SSL3_RANDOM_DATA" // CLASS_SSL3_RANDOM_DATA is used by CLASS_SSL3_MASTER_KEY_DERIVE_PARAMS #define CLASS_SSL3_KEY_MAT_OUT "sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_OUT" diff --git a/test/jdk/com/sun/crypto/provider/DHKEM/Compliance.java b/test/jdk/com/sun/crypto/provider/DHKEM/Compliance.java index 22c5c89b57be2..d8814513b1268 100644 --- a/test/jdk/com/sun/crypto/provider/DHKEM/Compliance.java +++ b/test/jdk/com/sun/crypto/provider/DHKEM/Compliance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -31,7 +31,6 @@ * @run main/othervm Compliance */ import jdk.test.lib.Asserts; -import jdk.test.lib.Utils; import javax.crypto.DecapsulateException; import javax.crypto.KEM; @@ -41,9 +40,7 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.*; import java.util.Arrays; -import java.util.Objects; import java.util.Random; -import java.util.function.Consumer; import com.sun.crypto.provider.DHKEM; @@ -66,12 +63,10 @@ public static void main(String[] args) throws Exception { private static void conform() { new KEM.Encapsulated(new SecretKeySpec(new byte[1], "X"), new byte[0], new byte[0]); new KEM.Encapsulated(new SecretKeySpec(new byte[1], "X"), new byte[0], null); - Utils.runAndCheckException( - () -> new KEM.Encapsulated(null, new byte[0], null), - NullPointerException.class); - Utils.runAndCheckException( - () -> new KEM.Encapsulated(new SecretKeySpec(new byte[1], "X"), null, null), - NullPointerException.class); + Asserts.assertThrows(NullPointerException.class, + () -> new KEM.Encapsulated(null, new byte[0], null)); + Asserts.assertThrows(NullPointerException.class, + () -> new KEM.Encapsulated(new SecretKeySpec(new byte[1], "X"), null, null)); } // basic should and shouldn't behaviors @@ -86,37 +81,33 @@ static void basic() throws Exception { KEM.getInstance("DHKEM", (String) null); KEM.getInstance("DHKEM", (Provider) null); KEM kem = KEM.getInstance("DHKEM"); - Utils.runAndCheckException( - () -> KEM.getInstance("OLALA"), - NoSuchAlgorithmException.class); - Utils.runAndCheckException( - () -> KEM.getInstance("DHKEM", "NoWhere"), - NoSuchProviderException.class); - Utils.runAndCheckException( - () -> KEM.getInstance("DHKEM", "SunRsaSign"), - NoSuchAlgorithmException.class); - - Utils.runAndCheckException( - () -> kem.newEncapsulator(null), - InvalidKeyException.class); - Utils.runAndCheckException( - () -> kem.newDecapsulator(null), - InvalidKeyException.class); + Asserts.assertThrows(NoSuchAlgorithmException.class, + () -> KEM.getInstance("OLALA")); + Asserts.assertThrows(NoSuchProviderException.class, + () -> KEM.getInstance("DHKEM", "NoWhere")); + Asserts.assertThrows(NoSuchAlgorithmException.class, + () -> KEM.getInstance("DHKEM", "SunRsaSign")); + + Asserts.assertThrows(InvalidKeyException.class, + () -> kem.newEncapsulator(null)); + Asserts.assertThrows(InvalidKeyException.class, + () -> kem.newDecapsulator(null)); // Still an EC key, rejected by implementation - Utils.runAndCheckException( - () -> kem.newEncapsulator(badECKey()), - ExChecker.of(InvalidKeyException.class).by(DHKEM.class)); + checkThrownBy(Asserts.assertThrows( + InvalidKeyException.class, + () -> kem.newEncapsulator(badECKey())), + DHKEM.class.getName()); // Not an EC key at all, rejected by framework coz it's not // listed in "SupportedKeyClasses" in SunJCE.java. - Utils.runAndCheckException( - () -> kem.newEncapsulator(kpRSA.getPublic()), - ExChecker.of(InvalidKeyException.class).by(KEM.class.getName() + "$DelayedKEM")); + checkThrownBy(Asserts.assertThrows( + InvalidKeyException.class, + () -> kem.newEncapsulator(kpRSA.getPublic())), + KEM.class.getName() + "$DelayedKEM"); - Utils.runAndCheckException( - () -> kem.newDecapsulator(kpRSA.getPrivate()), - InvalidKeyException.class); + Asserts.assertThrows(InvalidKeyException.class, + () -> kem.newDecapsulator(kpRSA.getPrivate())); kem.newEncapsulator(kpX.getPublic(), null); kem.newEncapsulator(kpX.getPublic(), null, null); @@ -125,15 +116,12 @@ static void basic() throws Exception { Asserts.assertEQ(enc1.key().getEncoded().length, e2.secretSize()); Asserts.assertEQ(enc1.key().getAlgorithm(), "AES"); - Utils.runAndCheckException( - () -> e2.encapsulate(-1, 12, "AES"), - IndexOutOfBoundsException.class); - Utils.runAndCheckException( - () -> e2.encapsulate(0, e2.secretSize() + 1, "AES"), - IndexOutOfBoundsException.class); - Utils.runAndCheckException( - () -> e2.encapsulate(0, e2.secretSize(), null), - NullPointerException.class); + Asserts.assertThrows(IndexOutOfBoundsException.class, + () -> e2.encapsulate(-1, 12, "AES")); + Asserts.assertThrows(IndexOutOfBoundsException.class, + () -> e2.encapsulate(0, e2.secretSize() + 1, "AES")); + Asserts.assertThrows(NullPointerException.class, + () -> e2.encapsulate(0, e2.secretSize(), null)); KEM.Encapsulated enc = e2.encapsulate(); Asserts.assertEQ(enc.key().getEncoded().length, e2.secretSize()); @@ -162,29 +150,23 @@ static void basic() throws Exception { d.secretSize() - 16, d.secretSize(), "AES"); Asserts.assertEQ(encTail.key(), decTail); - Utils.runAndCheckException( - () -> d.decapsulate(null), - NullPointerException.class); - Utils.runAndCheckException( - () -> d.decapsulate(enc.encapsulation(), -1, 12, "AES"), - IndexOutOfBoundsException.class); - Utils.runAndCheckException( - () -> d.decapsulate(enc.encapsulation(), 0, d.secretSize() + 1, "AES"), - IndexOutOfBoundsException.class); - Utils.runAndCheckException( - () -> d.decapsulate(enc.encapsulation(), 0, d.secretSize(), null), - NullPointerException.class); + Asserts.assertThrows(NullPointerException.class, + () -> d.decapsulate(null)); + Asserts.assertThrows(IndexOutOfBoundsException.class, + () -> d.decapsulate(enc.encapsulation(), -1, 12, "AES")); + Asserts.assertThrows(IndexOutOfBoundsException.class, + () -> d.decapsulate(enc.encapsulation(), 0, d.secretSize() + 1, "AES")); + Asserts.assertThrows(NullPointerException.class, + () -> d.decapsulate(enc.encapsulation(), 0, d.secretSize(), null)); KEM.Encapsulator e3 = kem.newEncapsulator(kpEC.getPublic()); KEM.Encapsulated enc2 = e3.encapsulate(); KEM.Decapsulator d3 = kem.newDecapsulator(kpX.getPrivate()); - Utils.runAndCheckException( - () -> d3.decapsulate(enc2.encapsulation()), - DecapsulateException.class); + Asserts.assertThrows(DecapsulateException.class, + () -> d3.decapsulate(enc2.encapsulation())); - Utils.runAndCheckException( - () -> d3.decapsulate(new byte[100]), - DecapsulateException.class); + Asserts.assertThrows(DecapsulateException.class, + () -> d3.decapsulate(new byte[100])); } static class MySecureRandom extends SecureRandom { @@ -273,34 +255,8 @@ public ECParameterSpec getParams() { }; } - // Used by Utils.runAndCheckException. Checks for type and final thrower. - record ExChecker(Class ex, String caller) - implements Consumer { - ExChecker { - Objects.requireNonNull(ex); - } - static ExChecker of(Class ex) { - return new ExChecker(ex, null); - } - ExChecker by(String caller) { - return new ExChecker(ex(), caller); - } - ExChecker by(Class caller) { - return new ExChecker(ex(), caller.getName()); - } - @Override - public void accept(Throwable t) { - if (t == null) { - throw new AssertionError("no exception thrown"); - } else if (!ex.isAssignableFrom(t.getClass())) { - throw new AssertionError("exception thrown is " + t.getClass()); - } else if (caller == null) { - return; - } else if (t.getStackTrace()[0].getClassName().equals(caller)) { - return; - } else { - throw new AssertionError("thrown by " + t.getStackTrace()[0].getClassName()); - } - } + // Ensures `t` is thrown by `caller` + static void checkThrownBy(T t, String caller) { + Asserts.assertEquals(caller, t.getStackTrace()[0].getClassName()); } } diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestSlice.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestSlice.java new file mode 100644 index 0000000000000..267d24a5b9f40 --- /dev/null +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestSlice.java @@ -0,0 +1,92 @@ +/* + * 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. + */ + +/* + * @test + * @library /test/lib .. + * @modules jdk.crypto.cryptoki + * java.base/sun.security.util + * @run main/othervm TestSlice p11-nss-sensitive.txt + * @enablePreview + */ + +import jdk.test.lib.Asserts; +import sun.security.util.SliceableSecretKey; + +import javax.crypto.Mac; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.security.*; +import java.util.Arrays; + +public class TestSlice extends PKCS11Test { + public static void main(String[] args) throws Exception { + main(new TestSlice(args[0]), args); + } + + public TestSlice(String conf) throws IOException { + copyNssCertKeyToClassesDir(); + setCommonSystemProps(); + System.setProperty("CUSTOM_P11_CONFIG", + Path.of(System.getProperty("test.src", "."), "../nss/" + conf).toString()); + System.setProperty("TOKEN", "nss"); + System.setProperty("TEST", "basic"); + } + + @Override + public void main(Provider p) throws Exception { + var data = new byte[48]; + for (var i = 0; i < 48; i++) data[i] = (byte)i; + var sk = new SecretKeySpec(data, "Generic"); + var hk = SecretKeyFactory.getInstance("Generic", p).translateKey(sk); + if (hk instanceof SliceableSecretKey ssk) { + var msg = "hello".getBytes(StandardCharsets.UTF_8); + for (int i = 0; i < 16; i++) { + var slice = ssk.slice("HmacSHA256", i, i + 32); + System.out.println(slice); + var enc = slice.getEncoded(); + var expected = Arrays.copyOfRange(data, i, i + 32); + if (enc != null) { + // If extractable, just compare key material + Asserts.assertEqualsByteArray(expected, enc); + } else { + // Otherwise, see if they are equivalent as Hmac key + var h1 = Mac.getInstance("HmacSHA256"); + h1.init(new SecretKeySpec(expected, "HmacSHA256")); + h1.update(msg); + var m1 = h1.doFinal(); + var h2 = Mac.getInstance("HmacSHA256", p); + h2.init(slice); + h2.update(msg); + var m2 = h2.doFinal(); + Asserts.assertEqualsByteArray(m1, m2); + } + } + } else { + throw new Exception("This should be a SliceableSecretKey"); + } + } +} diff --git a/test/jdk/sun/security/pkcs11/ec/DHKEM.java b/test/jdk/sun/security/pkcs11/ec/DHKEM.java new file mode 100644 index 0000000000000..ee8556b846c55 --- /dev/null +++ b/test/jdk/sun/security/pkcs11/ec/DHKEM.java @@ -0,0 +1,114 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8325448 + * @summary Verify that HPKE works across security providers + * @library /test/lib .. + * @modules jdk.crypto.cryptoki + * @run main/othervm DHKEM p11-nss.txt + * @run main/othervm DHKEM p11-nss-sensitive.txt + * @enablePreview + */ + +import jdk.test.lib.Asserts; + +import javax.crypto.Cipher; +import javax.crypto.KDF; +import javax.crypto.KEM; +import javax.crypto.spec.HKDFParameterSpec; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.security.*; +import java.security.spec.ECGenParameterSpec; + +public class DHKEM extends PKCS11Test { + + public static void main(String[] args) throws Exception { + main(new DHKEM(args[0]), args); + } + + public DHKEM(String conf) throws IOException { + copyNssCertKeyToClassesDir(); + setCommonSystemProps(); + System.setProperty("CUSTOM_P11_CONFIG", + Path.of(System.getProperty("test.src", "."), "../nss/" + conf).toString()); + System.setProperty("TOKEN", "nss"); + System.setProperty("TEST", "basic"); + } + + @Override + public void main(Provider p) throws Exception { + var ec = Security.getProvider("SunEC"); + test(p, p); + test(p, ec); + test(ec, p); + } + + static void test(Provider p1, Provider p2) throws Exception { + var g = KeyPairGenerator.getInstance("EC", p2); + g.initialize(new ECGenParameterSpec("secp521r1")); + var kp = g.generateKeyPair(); + var msg = "hello".getBytes(StandardCharsets.UTF_8); + + prefer(p1); + var kem1 = KEM.getInstance("DHKEM"); + var e = kem1.newEncapsulator(kp.getPublic()); + var enc = e.encapsulate(); + var kdf1 = KDF.getInstance("HKDF-SHA256"); + var k1 = kdf1.deriveKey("AES", HKDFParameterSpec.ofExtract().addIKM(enc.key()).thenExpand(null, 32)); + var c1 = Cipher.getInstance("AES"); + c1.init(Cipher.ENCRYPT_MODE, k1); + var ct = c1.doFinal(msg); + + Asserts.assertTrue(e.secretSize() >= 42); + var enc2 = e.encapsulate(5, 37, "AES"); + c1.init(Cipher.ENCRYPT_MODE, enc2.key()); + var ct2 = c1.doFinal(msg); + + prefer(p2); + var kem2 = KEM.getInstance("DHKEM"); + var d = kem2.newDecapsulator(kp.getPrivate()); + var k = d.decapsulate(enc.encapsulation()); + var kdf2 = KDF.getInstance("HKDF-SHA256"); + var k2 = kdf2.deriveKey("AES", HKDFParameterSpec.ofExtract().addIKM(k).thenExpand(null, 32)); + var c2 = Cipher.getInstance("AES"); + c2.init(Cipher.DECRYPT_MODE, k2); + var pt = c2.doFinal(ct); + + Asserts.assertEqualsByteArray(msg, pt); + + var k3 = d.decapsulate(enc2.encapsulation(), 5, 37, "AES"); + c2.init(Cipher.DECRYPT_MODE, k3); + var pt2 = c2.doFinal(ct2); + + Asserts.assertEqualsByteArray(msg, pt2); + } + + static void prefer(Provider p) { + Security.removeProvider(p.getName()); + Security.insertProviderAt(p, 1); + } +} From e8c2f8a7ab56b887d22516b7eb7705df129ed0db Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Fri, 4 Apr 2025 15:52:35 -0400 Subject: [PATCH 3/3] revert auth support --- .../com/sun/crypto/provider/DHKEM.java | 74 ++++--------------- 1 file changed, 15 insertions(+), 59 deletions(-) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java index 1c08ede5b68f7..e07e73f7baf8b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java @@ -71,6 +71,7 @@ import sun.security.util.SliceableSecretKey; // Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html, +// without the AuthEncap and AuthDecap functions public class DHKEM implements KEMSpi { private static final byte[] KEM = new byte[] @@ -90,8 +91,7 @@ public class DHKEM implements KEMSpi { private static final byte[] EMPTY = new byte[0]; private record Handler(Params params, SecureRandom secureRandom, - PrivateKey skS, PublicKey pkS, // sender keys - PrivateKey skR, PublicKey pkR) // receiver keys + PrivateKey skR, PublicKey pkR) implements EncapsulatorSpi, DecapsulatorSpi { @Override @@ -103,18 +103,10 @@ public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { PublicKey pkE = kpE.getPublic(); byte[] pkEm = params.SerializePublicKey(pkE); byte[] pkRm = params.SerializePublicKey(pkR); + byte[] kem_context = concat(pkEm, pkRm); try { - SecretKey key; - if (skS == null) { - byte[] kem_context = concat(pkEm, pkRm); - key = params.deriveKey(algorithm, from, to, kem_context, - params.DH(skE, pkR)); - } else { - byte[] pkSm = params.SerializePublicKey(pkS); - byte[] kem_context = concat(pkEm, pkRm, pkSm); - key = params.deriveKey(algorithm, from, to, kem_context, - params.DH(skE, pkR), params.DH(skS, pkR)); - } + SecretKey key = params.deriveKey(algorithm, from, to, kem_context, + params.DH(skE, pkR)); return new KEM.Encapsulated(key, pkEm, null); } catch (UnsupportedOperationException e) { throw e; @@ -135,16 +127,9 @@ public SecretKey engineDecapsulate(byte[] encapsulation, try { PublicKey pkE = params.DeserializePublicKey(encapsulation); byte[] pkRm = params.SerializePublicKey(pkR); - if (pkS == null) { - byte[] kem_context = concat(encapsulation, pkRm); - return params.deriveKey(algorithm, from, to, kem_context, - params.DH(skR, pkE)); - } else { - byte[] pkSm = params.SerializePublicKey(pkS); - byte[] kem_context = concat(encapsulation, pkRm, pkSm); - return params.deriveKey(algorithm, from, to, kem_context, - params.DH(skR, pkE), params.DH(skR, pkS)); - } + byte[] kem_context = concat(encapsulation, pkRm); + return params.deriveKey(algorithm, from, to, kem_context, + params.DH(skR, pkE)); } catch (UnsupportedOperationException e) { throw e; } catch (IOException | InvalidKeyException e) { @@ -294,14 +279,14 @@ private SecretKey DH(PrivateKey skE, PublicKey pkR) // or the decapsulator. The key slicing is implemented inside. // Throws UOE if a slice of the key cannot be found. private SecretKey deriveKey(String alg, int from, int to, - byte[] kem_context, SecretKey... dhs) + byte[] kem_context, SecretKey dh) throws NoSuchAlgorithmException { if (from == 0 && to == Nsecret) { - return ExtractAndExpand(kem_context, alg, dhs); + return ExtractAndExpand(kem_context, alg, dh); } else { // First get shared secrets in "Generic" and then get a slice // of it in the requested algorithm. - var fullKey = ExtractAndExpand(kem_context, "Generic", dhs); + var fullKey = ExtractAndExpand(kem_context, "Generic", dh); if ("RAW".equalsIgnoreCase(fullKey.getFormat())) { byte[] km = fullKey.getEncoded(); if (km == null) { @@ -318,11 +303,11 @@ private SecretKey deriveKey(String alg, int from, int to, } } - private SecretKey ExtractAndExpand(byte[] kem_context, String alg, SecretKey... dhs) + private SecretKey ExtractAndExpand(byte[] kem_context, String alg, SecretKey dh) throws NoSuchAlgorithmException { var kdf = KDF.getInstance(hkdfAlgorithm); var builder = labeledExtract(suiteId, EAE_PRK); - for (var dh : dhs) builder.addIKM(dh); + builder.addIKM(dh); try { return kdf.deriveKey(alg, labeledExpand(builder, suiteId, SHARED_SECRET, kem_context, Nsecret)); @@ -408,22 +393,7 @@ public EncapsulatorSpi engineNewEncapsulator( throw new InvalidAlgorithmParameterException("no spec needed"); } Params params = paramsFromKey(pk); - return new Handler(params, getSecureRandom(secureRandom), null, null, null, pk); - } - - // AuthEncap is not public KEM API - public EncapsulatorSpi engineNewAuthEncapsulator(PublicKey pkR, PrivateKey skS, - AlgorithmParameterSpec spec, SecureRandom secureRandom) - throws InvalidAlgorithmParameterException, InvalidKeyException { - if (pkR == null || skS == null) { - throw new InvalidKeyException("input key is null"); - } - if (spec != null) { - throw new InvalidAlgorithmParameterException("no spec needed"); - } - Params params = paramsFromKey(pkR); - return new Handler(params, getSecureRandom(secureRandom), - skS, params.getPublicKey(skS), null, pkR); + return new Handler(params, getSecureRandom(secureRandom), null, pk); } @Override @@ -436,21 +406,7 @@ public DecapsulatorSpi engineNewDecapsulator(PrivateKey sk, AlgorithmParameterSp throw new InvalidAlgorithmParameterException("no spec needed"); } Params params = paramsFromKey(sk); - return new Handler(params, null, null, null, sk, params.getPublicKey(sk)); - } - - // AuthDecap is not public KEM API - public DecapsulatorSpi engineNewAuthDecapsulator( - PrivateKey skR, PublicKey pkS, AlgorithmParameterSpec spec) - throws InvalidAlgorithmParameterException, InvalidKeyException { - if (skR == null || pkS == null) { - throw new InvalidKeyException("input key is null"); - } - if (spec != null) { - throw new InvalidAlgorithmParameterException("no spec needed"); - } - Params params = paramsFromKey(skR); - return new Handler(params, null, null, pkS, skR, params.getPublicKey(skR)); + return new Handler(params, null, sk, params.getPublicKey(sk)); } private Params paramsFromKey(AsymmetricKey k) throws InvalidKeyException {