From 06cfc6c29470612704121cfdd039699143aa297e Mon Sep 17 00:00:00 2001 From: Kostas Tsiounis Date: Tue, 14 May 2024 16:21:44 -0400 Subject: [PATCH] Get native EC key pointer during init through ECUtil instead of key impl The acquisition of a native pointer for an EC key, private or public, is being moved to the ECUtil, instead of the implementation classes of said keys. This allows use of interop keys, rather than just keys coming from SunEC. The methods to get the pointers to the native keys are called during the initialization phases, to discover the possibility of not being able to utilize the native code and revert to the Java implementation early. The engineUpdate() methods of the RawECDSA subclass are, also, updated to use the java implementation, if one has been initialized. Signed-off by: Kostas Tsiounis --- .../security/ec/NativeECDHKeyAgreement.java | 89 +++++++------ .../sun/security/ec/NativeECDSASignature.java | 121 +++++++++--------- .../classes/sun/security/ec/NativeECUtil.java | 61 ++++++++- .../sun/security/ec/ECPrivateKeyImpl.java | 42 ------ .../sun/security/ec/ECPublicKeyImpl.java | 50 +------- 5 files changed, 166 insertions(+), 197 deletions(-) diff --git a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDHKeyAgreement.java b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDHKeyAgreement.java index 0e7cd3388e8..fca12007b63 100644 --- a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDHKeyAgreement.java +++ b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDHKeyAgreement.java @@ -40,6 +40,7 @@ import java.security.ProviderException; import java.security.SecureRandom; import java.security.interfaces.ECKey; +import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; @@ -74,13 +75,19 @@ public final class NativeECDHKeyAgreement extends KeyAgreementSpi { private static final Map curveSupported = new ConcurrentHashMap<>(); /* private key, if initialized */ - private ECPrivateKeyImpl privateKey; + private ECPrivateKey privateKey; + + /* pointer to native private key, if initialized */ + private long nativePrivateKey; /* operations associated with private key, if initialized */ private ECOperations privateKeyOps; /* public key, non-null between doPhase() & generateSecret() only */ - private ECPublicKeyImpl publicKey; + private ECPublicKey publicKey; + + /* pointer to native public key, if initialized */ + private long nativePublicKey; /* the type of EC curve */ private String curve; @@ -109,16 +116,26 @@ private void init(Key key) } /* attempt to translate the key if it is not an ECKey */ ECKey ecKey = ECKeyFactory.toECKey(key); - if (ecKey instanceof ECPrivateKeyImpl keyImpl) { + if (ecKey instanceof ECPrivateKey ecPrivateKey) { Optional opsOpt = - ECOperations.forParameters(keyImpl.getParams()); + ECOperations.forParameters(ecPrivateKey.getParams()); if (opsOpt.isEmpty()) { - NamedCurve nc = CurveDB.lookup(keyImpl.getParams()); + NamedCurve nc = CurveDB.lookup(ecPrivateKey.getParams()); throw new InvalidAlgorithmParameterException( "Curve not supported: " + ((nc != null) ? nc.toString() : "unknown")); } - this.privateKey = keyImpl; + + this.privateKey = ecPrivateKey; + this.nativePrivateKey = NativeECUtil.getPrivateKeyNativePtr(ecPrivateKey); + if (this.nativePrivateKey == -1) { + if (nativeCryptTrace) { + System.err.println("Init: Could not create a pointer to a native private key." + + " Using Java implementation."); + } + this.initializeJavaImplementation(key); + return; + } this.privateKeyOps = opsOpt.get(); ECParameterSpec params = this.privateKey.getParams(); @@ -132,8 +149,8 @@ private void init(Key key) boolean absent = NativeECUtil.putCurveIfAbsent("ECKeyImpl", Boolean.FALSE); /* only print the first time a curve is used */ if (absent && nativeCryptTrace) { - System.err.println("Only ECPrivateKeyImpl and ECPublicKeyImpl" + - " are supported by the native implementation, " + + System.err.println("Only ECPrivateKey" + + " is supported by the native implementation, " + "using Java crypto implementation for key agreement."); } this.initializeJavaImplementation(key); @@ -184,24 +201,26 @@ protected Key engineDoPhase(Key key, boolean lastPhase) // Validate public key. validate(privateKeyOps, ecKey); - if (ecKey instanceof ECPublicKeyImpl keyImpl) { - this.publicKey = keyImpl; - - int keyLenBits = this.publicKey.getParams().getCurve().getField().getFieldSize(); - this.secretLen = (keyLenBits + 7) >> 3; - - return null; - } else { - boolean absent = NativeECUtil.putCurveIfAbsent("ECKeyImpl", Boolean.FALSE); - /* only print the first time a curve is used */ - if (absent && nativeCryptTrace) { - System.err.println("Only ECPrivateKeyImpl and ECPublicKeyImpl" + - " are supported by the native implementation, " + - "using Java crypto implementation for key agreement."); + this.publicKey = ecKey; + this.nativePublicKey = NativeECUtil.getPublicKeyNativePtr(ecKey); + if (this.nativePublicKey == -1) { + if (nativeCryptTrace) { + System.err.println("DoPhase: Could not create a pointer to a native public key." + + " Using Java implementation."); + } + try { + this.initializeJavaImplementation(this.privateKey); + this.javaImplementation.engineDoPhase(ecKey, true); + } catch (InvalidKeyException e) { + /* should not happen */ + throw new InternalError(e); } - this.initializeJavaImplementation(this.privateKey); - return this.javaImplementation.engineDoPhase(key, lastPhase); } + + int keyLenBits = this.publicKey.getParams().getCurve().getField().getFieldSize(); + this.secretLen = (keyLenBits + 7) >> 3; + + return null; } // Verify that x and y are integers in the interval [0, p - 1]. @@ -297,27 +316,7 @@ protected int engineGenerateSecret(byte[] sharedSecret, int offset) if ((this.privateKey == null) || (this.publicKey == null)) { throw new IllegalStateException("Not initialized correctly"); } - long nativePublicKey = this.publicKey.getNativePtr(); - long nativePrivateKey = this.privateKey.getNativePtr(); - if ((nativePublicKey == -1) || (nativePrivateKey == -1)) { - absent = NativeECUtil.putCurveIfAbsent(this.curve, Boolean.FALSE); - if (!absent) { - throw new ProviderException("Could not convert keys to native format"); - } - /* only print the first time a curve is used */ - if (nativeCryptTrace) { - System.err.println(this.curve + - " is not supported by OpenSSL, using Java crypto implementation for preparing agreement."); - } - try { - this.initializeJavaImplementation(this.privateKey); - this.javaImplementation.engineDoPhase(this.publicKey, true); - } catch (InvalidKeyException e) { - /* should not happen */ - throw new InternalError(e); - } - return this.javaImplementation.engineGenerateSecret(sharedSecret, offset); - } + absent = NativeECUtil.putCurveIfAbsent(this.curve, Boolean.TRUE); if (absent && nativeCryptTrace) { System.err.println(this.curve + diff --git a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java index e028e50813b..0b999df3feb 100644 --- a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java +++ b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECDSASignature.java @@ -102,14 +102,14 @@ abstract class NativeECDSASignature extends SignatureSpi { // private key, if initialized for signing private ECPrivateKey privateKey; - // private key impl, if initialized for signing - private ECPrivateKeyImpl privateKeyImpl; + // native private key pointer, if initialized for signing + private long nativePrivateKey; // public key, if initialized for verifying private ECPublicKey publicKey; - // public key impl, if initialized for verifying - private ECPublicKeyImpl publicKeyImpl; + // native public key pointer, if initialized for verifying + private long nativePublicKey; // signature parameters private ECParameterSpec sigParams; @@ -118,7 +118,7 @@ abstract class NativeECDSASignature extends SignatureSpi { private final boolean p1363Format; // the Java implementation, if needed - private ECDSASignature javaImplementation; + ECDSASignature javaImplementation; /** * Constructs a new NativeECDSASignature. @@ -182,51 +182,62 @@ static class RawECDSA extends NativeECDSASignature { // Stores the precomputed message digest value. @Override protected void engineUpdate(byte b) throws SignatureException { - if (offset >= precomputedDigest.length) { - offset = RAW_ECDSA_MAX + 1; - return; + if (this.javaImplementation != null) { + this.javaImplementation.engineUpdate(b); + } else { + if (offset >= precomputedDigest.length) { + offset = RAW_ECDSA_MAX + 1; + return; + } + precomputedDigest[offset++] = b; } - precomputedDigest[offset++] = b; } // Stores the precomputed message digest value. @Override protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { - if (offset >= precomputedDigest.length) { - offset = RAW_ECDSA_MAX + 1; - return; + if (this.javaImplementation != null) { + this.javaImplementation.engineUpdate(b, off, len); + } else { + if (offset >= precomputedDigest.length) { + offset = RAW_ECDSA_MAX + 1; + return; + } + System.arraycopy(b, off, precomputedDigest, offset, len); + offset += len; } - System.arraycopy(b, off, precomputedDigest, offset, len); - offset += len; } // Stores the precomputed message digest value. @Override protected void engineUpdate(ByteBuffer byteBuffer) { - int len = byteBuffer.remaining(); - if (len <= 0) { - return; - } - if (len >= (precomputedDigest.length - offset)) { - offset = RAW_ECDSA_MAX + 1; - return; + if (this.javaImplementation != null) { + this.javaImplementation.engineUpdate(byteBuffer); + } else { + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + if (len >= (precomputedDigest.length - offset)) { + offset = RAW_ECDSA_MAX + 1; + return; + } + byteBuffer.get(precomputedDigest, offset, len); + offset += len; } - byteBuffer.get(precomputedDigest, offset, len); - offset += len; } @Override - protected void resetDigest() { + void resetDigest() { offset = 0; } // Returns the precomputed message digest value. @Override - protected byte[] getDigestValue() throws SignatureException { + byte[] getDigestValue() throws SignatureException { if (offset > RAW_ECDSA_MAX) { throw new SignatureException("Message digest is too long"); - } byte[] result = new byte[offset]; System.arraycopy(precomputedDigest, 0, result, 0, offset); @@ -391,16 +402,19 @@ protected void engineInitVerify(PublicKey publicKey) this.privateKey = null; resetDigest(); - if (key instanceof ECPublicKeyImpl keyImpl) { - this.publicKeyImpl = keyImpl; - this.privateKeyImpl = null; - this.javaImplementation = null; - if (nativeCryptTrace) { - System.err.println("InitVerify: Using native crypto implementation for verifying signature."); - } - } else { + this.nativePublicKey = NativeECUtil.getPublicKeyNativePtr(key); + if (this.nativePublicKey == -1) { this.javaImplementation = getJavaInstance(); this.javaImplementation.engineInitVerify(publicKey); + if (nativeCryptTrace) { + System.err.println("InitVerify: Could not create a pointer to a native key." + + " Using Java implementation."); + } + return; + } + this.javaImplementation = null; + if (nativeCryptTrace) { + System.err.println("InitVerify: Keys were successfully converted to native OpenSSL format."); } } @@ -449,23 +463,26 @@ protected void engineInitSign(PrivateKey privateKey, SecureRandom random) this.random = random; resetDigest(); - if (key instanceof ECPrivateKeyImpl keyImpl) { - this.publicKeyImpl = null; - this.privateKeyImpl = keyImpl; - this.javaImplementation = null; - if (nativeCryptTrace) { - System.err.println("InitSign: Using native crypto implementation for verifying signature."); - } - } else { + this.nativePrivateKey = NativeECUtil.getPrivateKeyNativePtr(key); + if (this.nativePrivateKey == -1) { this.javaImplementation = getJavaInstance(); this.javaImplementation.engineInitSign(privateKey, random); + if (nativeCryptTrace) { + System.err.println("InitSign: Could not create a pointer to a native key." + + " Using Java implementation."); + } + return; + } + this.javaImplementation = null; + if (nativeCryptTrace) { + System.err.println("InitSign: Keys were successfully converted to native OpenSSL format."); } } /** * Resets the message digest if needed. */ - protected void resetDigest() { + void resetDigest() { if (needsReset) { if (messageDigest != null) { messageDigest.reset(); @@ -477,7 +494,7 @@ protected void resetDigest() { /** * Returns the message digest value. */ - protected byte[] getDigestValue() throws SignatureException { + byte[] getDigestValue() throws SignatureException { needsReset = false; return messageDigest.digest(); } @@ -537,7 +554,6 @@ protected byte[] engineSign() throws SignatureException { return this.javaImplementation.engineSign(); } - long nativePrivateKey = privateKeyImpl.getNativePtr(); byte[] digest = getDigestValue(); int digestLen = digest.length; ECParameterSpec params = privateKey.getParams(); @@ -547,13 +563,6 @@ protected byte[] engineSign() throws SignatureException { ECDSAOperations.forParameters(params) .orElseThrow(() -> new SignatureException("Curve not supported: " + params)); - if (nativePrivateKey == -1) { - throw new ProviderException("Keys could not be converted to native OpenSSL format"); - } - if (nativeCryptTrace) { - System.err.println("Sign: Keys were successfully converted to native OpenSSL format."); - } - if (nativeCrypto == null) { nativeCrypto = NativeCrypto.getNativeCrypto(); } @@ -604,14 +613,6 @@ protected boolean engineVerify(byte[] signature) throws SignatureException { } } - long nativePublicKey = publicKeyImpl.getNativePtr(); - if (nativePublicKey == -1) { - throw new ProviderException("Could not convert keys to native format"); - } - if (nativeCryptTrace) { - System.err.println("Verify: Keys were successfully converted to native OpenSSL format."); - } - if (nativeCrypto == null) { nativeCrypto = NativeCrypto.getNativeCrypto(); } diff --git a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECUtil.java b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECUtil.java index 0e4fb7575c6..4476dbb2c0f 100644 --- a/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECUtil.java +++ b/closed/src/jdk.crypto.ec/share/classes/sun/security/ec/NativeECUtil.java @@ -25,7 +25,7 @@ /* * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * (c) Copyright IBM Corp. 2022, 2024 All Rights Reserved * =========================================================================== */ @@ -35,6 +35,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.math.BigInteger; import java.security.ProviderException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidParameterSpecException; import java.security.spec.ECPoint; @@ -158,4 +160,61 @@ static long encodeGroup(ECParameterSpec params) { n, n.length, h, h.length); } + + /** + * Returns the native pointer for the provided EC public key. + * + * @param key the EC public key + * @return the native EC public key context pointer or -1 on error + */ + static long getPublicKeyNativePtr(ECPublicKey key) { + synchronized (key) { + long nativePointer = encodeGroup(key.getParams()); + if (nativePointer != -1) { + try { + byte[] x = key.getW().getAffineX().toByteArray(); + byte[] y = key.getW().getAffineY().toByteArray(); + int fieldType = NativeCrypto.ECField_Fp; + if (key.getParams().getCurve().getField() instanceof ECFieldF2m) { + fieldType = NativeCrypto.ECField_F2m; + } + if (nativeCrypto.ECCreatePublicKey(nativePointer, x, x.length, y, y.length, fieldType) == -1) { + nativeCrypto.ECDestroyKey(nativePointer); + nativePointer = -1; + } + } finally { + if (nativePointer != -1) { + nativeCrypto.createECKeyCleaner(key, nativePointer); + } + } + } + return nativePointer; + } + } + + /** + * Returns the native pointer for the provided EC private key. + * + * @param key the EC private key + * @return the native EC private key context pointer or -1 on error + */ + static long getPrivateKeyNativePtr(ECPrivateKey key) { + synchronized (key) { + long nativePointer = encodeGroup(key.getParams()); + if (nativePointer != -1) { + try { + byte[] value = key.getS().toByteArray(); + if (nativeCrypto.ECCreatePrivateKey(nativePointer, value, value.length) == -1) { + nativeCrypto.ECDestroyKey(nativePointer); + nativePointer = -1; + } + } finally { + if (nativePointer != -1) { + nativeCrypto.createECKeyCleaner(key, nativePointer); + } + } + } + return nativePointer; + } + } } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java index 411abfe91b8..88cde0c55df 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java @@ -23,12 +23,6 @@ * questions. */ -/* - * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved - * =========================================================================== - */ - package sun.security.ec; import java.io.IOException; @@ -41,8 +35,6 @@ import java.security.spec.*; import java.util.Arrays; -import jdk.crypto.jniprovider.NativeCrypto; - import sun.security.util.*; import sun.security.x509.AlgorithmId; import sun.security.pkcs.PKCS8Key; @@ -74,12 +66,10 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey { @java.io.Serial private static final long serialVersionUID = 88695385615075129L; - private static NativeCrypto nativeCrypto; private BigInteger s; // private value private byte[] arrayS; // private value as a little-endian array private ECParameterSpec params; - private long nativeECKey; /** * Construct a key from its encoding. Called by the ECKeyFactory. @@ -234,36 +224,4 @@ private void readObject(ObjectInputStream stream) throw new InvalidObjectException( "ECPrivateKeyImpl keys are not directly deserializable"); } - - /** - * Returns the native EC public key context pointer. - * @return the native EC public key context pointer or -1 on error - */ - long getNativePtr() { - if (this.nativeECKey == 0x0) { - synchronized (this) { - if (this.nativeECKey == 0x0) { - if (nativeCrypto == null) { - nativeCrypto = NativeCrypto.getNativeCrypto(); - } - long nativePointer = NativeECUtil.encodeGroup(this.params); - try { - if (nativePointer != -1) { - byte[] value = this.getS().toByteArray(); - if (nativeCrypto.ECCreatePrivateKey(nativePointer, value, value.length) == -1) { - nativeCrypto.ECDestroyKey(nativePointer); - nativePointer = -1; - } - } - } finally { - if (nativePointer != -1) { - nativeCrypto.createECKeyCleaner(this, nativePointer); - } - } - this.nativeECKey = nativePointer; - } - } - } - return this.nativeECKey; - } } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPublicKeyImpl.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPublicKeyImpl.java index b2d11514328..c88fa450734 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPublicKeyImpl.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPublicKeyImpl.java @@ -23,16 +23,9 @@ * questions. */ -/* - * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved - * =========================================================================== - */ - package sun.security.ec; import java.io.IOException; -import java.math.BigInteger; import java.io.InvalidObjectException; import java.io.ObjectInputStream; @@ -40,8 +33,6 @@ import java.security.interfaces.*; import java.security.spec.*; -import jdk.crypto.jniprovider.NativeCrypto; - import sun.security.util.ECParameters; import sun.security.util.ECUtil; @@ -57,11 +48,9 @@ public final class ECPublicKeyImpl extends X509Key implements ECPublicKey { @java.io.Serial private static final long serialVersionUID = -2462037275160462289L; - private static NativeCrypto nativeCrypto; private ECPoint w; private ECParameterSpec params; - private long nativeECKey; /** * Construct a key from its components. Used by the @@ -159,41 +148,4 @@ private void readObject(ObjectInputStream stream) throw new InvalidObjectException( "ECPublicKeyImpl keys are not directly deserializable"); } - - /** - * Returns the native EC public key context pointer. - * @return the native EC public key context pointer or -1 on error - */ - long getNativePtr() { - if (this.nativeECKey == 0x0) { - synchronized (this) { - if (this.nativeECKey == 0x0) { - if (nativeCrypto == null) { - nativeCrypto = NativeCrypto.getNativeCrypto(); - } - long nativePointer = NativeECUtil.encodeGroup(this.params); - try { - if (nativePointer != -1) { - byte[] x = this.w.getAffineX().toByteArray(); - byte[] y = this.w.getAffineY().toByteArray(); - int fieldType = NativeCrypto.ECField_Fp; - if (this.params.getCurve().getField() instanceof ECFieldF2m) { - fieldType = NativeCrypto.ECField_F2m; - } - if (nativeCrypto.ECCreatePublicKey(nativePointer, x, x.length, y, y.length, fieldType) == -1) { - nativeCrypto.ECDestroyKey(nativePointer); - nativePointer = -1; - } - } - } finally { - if (nativePointer != -1) { - nativeCrypto.createECKeyCleaner(this, nativePointer); - } - } - this.nativeECKey = nativePointer; - } - } - } - return this.nativeECKey; - } -} +} \ No newline at end of file