641 lines
20 KiB
Java
641 lines
20 KiB
Java
/* Ecc.java
|
|
*
|
|
* Copyright (C) 2006-2025 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
package com.wolfssl.wolfcrypt;
|
|
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.spec.EllipticCurve;
|
|
import java.security.spec.ECParameterSpec;
|
|
import java.security.spec.ECFieldFp;
|
|
|
|
import com.wolfssl.wolfcrypt.Rng;
|
|
|
|
/**
|
|
* Wrapper for the native WolfCrypt ECC implementation
|
|
*/
|
|
public class Ecc extends NativeStruct {
|
|
|
|
private WolfCryptState state = WolfCryptState.UNINITIALIZED;
|
|
|
|
/* used with native wc_ecc_set_rng() */
|
|
private Rng rng = null;
|
|
|
|
/* Do we own the Rng struct, or has that been passed in? Used
|
|
* during Rng cleanup. */
|
|
private boolean weOwnRng = true;
|
|
|
|
/** Lock around Rng object access */
|
|
private final Object rngLock = new Object();
|
|
|
|
/** Lock around object state */
|
|
protected final Object stateLock = new Object();
|
|
|
|
/**
|
|
* Create new Ecc object.
|
|
*
|
|
* @throws WolfCryptException if ECC has not been compiled into native
|
|
* wolfCrypt library.
|
|
*/
|
|
public Ecc() {
|
|
if (!FeatureDetect.EccEnabled()) {
|
|
throw new WolfCryptException(
|
|
WolfCryptError.NOT_COMPILED_IN.getCode());
|
|
}
|
|
/* Internal state is initialized on first use */
|
|
}
|
|
|
|
/**
|
|
* Create new Ecc object with existing Rng object.
|
|
*
|
|
* @param rng initialized com.wolfssl.wolfcrypt.Rng object
|
|
*
|
|
* @throws WolfCryptException if ECC has not been compiled into native
|
|
* wolfCrypt library.
|
|
*/
|
|
public Ecc(Rng rng) {
|
|
if (!FeatureDetect.EccEnabled()) {
|
|
throw new WolfCryptException(
|
|
WolfCryptError.NOT_COMPILED_IN.getCode());
|
|
}
|
|
|
|
/* Internal state is initialized on first use */
|
|
this.rng = rng;
|
|
weOwnRng = false;
|
|
}
|
|
|
|
@Override
|
|
public synchronized void releaseNativeStruct() {
|
|
|
|
synchronized (stateLock) {
|
|
if ((state != WolfCryptState.UNINITIALIZED) &&
|
|
(state != WolfCryptState.RELEASED)) {
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_free();
|
|
}
|
|
|
|
synchronized (rngLock) {
|
|
if (this.weOwnRng && this.rng != null) {
|
|
rng.free();
|
|
rng.releaseNativeStruct();
|
|
rng = null;
|
|
}
|
|
}
|
|
|
|
super.releaseNativeStruct();
|
|
state = WolfCryptState.RELEASED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Malloc native JNI ecc_key structure
|
|
*
|
|
* @return native allocated pointer
|
|
*
|
|
* @throws OutOfMemoryError when malloc fails with memory error
|
|
*/
|
|
protected native long mallocNativeStruct() throws OutOfMemoryError;
|
|
|
|
private native void wc_ecc_init();
|
|
private native void wc_ecc_free();
|
|
private native void wc_ecc_make_key(Rng rng, int size);
|
|
private native void wc_ecc_make_key_ex(Rng rng, int size, String curveName);
|
|
private native void wc_ecc_check_key();
|
|
private native byte[] wc_ecc_shared_secret(Ecc pubKey, Rng rng);
|
|
private native void wc_ecc_import_private(byte[] privKey, byte[] x963Key,
|
|
String curveName);
|
|
private native byte[] wc_ecc_export_private();
|
|
private native void wc_ecc_import_x963(byte[] key);
|
|
private native byte[] wc_ecc_export_x963();
|
|
private native void wc_EccPrivateKeyDecode(byte[] key);
|
|
private native byte[] wc_EccKeyToDer();
|
|
private native void wc_EccPublicKeyDecode(byte[] key);
|
|
private native byte[] wc_EccPublicKeyToDer();
|
|
private native byte[] wc_ecc_sign_hash(byte[] hash, Rng rng);
|
|
private native boolean wc_ecc_verify_hash(byte[] hash, byte[] signature);
|
|
private static native int wc_ecc_get_curve_size_from_name(String name);
|
|
private native byte[] wc_ecc_private_key_to_pkcs8();
|
|
private static native String wc_ecc_get_curve_name_from_id(int curve_id);
|
|
private static native int wc_ecc_get_curve_id_from_params(int fieldSize,
|
|
byte[] prime, byte[] Af, byte[] Bf, byte[] order,
|
|
byte[] Gx, byte[] Gy, int cofactor);
|
|
|
|
/**
|
|
* Internal helper method to initialize object if/when needed.
|
|
*
|
|
* @throws IllegalStateException on failure to initialize properly or
|
|
* if releaseNativeStruct() has been called and object has been
|
|
* released
|
|
*/
|
|
private synchronized void checkStateAndInitialize()
|
|
throws IllegalStateException {
|
|
|
|
synchronized (stateLock) {
|
|
if (state == WolfCryptState.RELEASED) {
|
|
throw new IllegalStateException("Object has been released");
|
|
}
|
|
|
|
if (state == WolfCryptState.UNINITIALIZED) {
|
|
init();
|
|
if (state != WolfCryptState.INITIALIZED) {
|
|
throw new IllegalStateException(
|
|
"Failed to initialize Object");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize Ecc object
|
|
*/
|
|
private void init() {
|
|
/* Allocate native struct pointer from NativeStruct */
|
|
initNativeStruct();
|
|
synchronized (pointerLock) {
|
|
wc_ecc_init();
|
|
}
|
|
|
|
/* used with native wc_ecc_set_rng() */
|
|
synchronized (rngLock) {
|
|
if (rng == null) {
|
|
rng = new Rng();
|
|
rng.init();
|
|
weOwnRng = true;
|
|
}
|
|
}
|
|
|
|
state = WolfCryptState.INITIALIZED;
|
|
}
|
|
|
|
/**
|
|
* Throw exception if key has been loaded already.
|
|
*
|
|
* @throws IllegalStateException if key has been loaded already
|
|
*/
|
|
private void throwIfKeyExists() throws IllegalStateException {
|
|
synchronized (stateLock) {
|
|
if (state == WolfCryptState.READY) {
|
|
throw new IllegalStateException("Object already has a key");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Throw exception if key has not been loaded.
|
|
*
|
|
* @throws IllegalStateException if key has not been loaded
|
|
*/
|
|
private void throwIfKeyNotLoaded() throws IllegalStateException {
|
|
synchronized (stateLock) {
|
|
if (state != WolfCryptState.READY) {
|
|
throw new IllegalStateException(
|
|
"No key available to perform the operation");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate ECC key
|
|
*
|
|
* @param rng initialized Rng object
|
|
* @param size size of key to generate
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void makeKey(Rng rng, int size)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_make_key(rng, size);
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Generate ECC key on specified curve
|
|
*
|
|
* @param rng initialized Rng object
|
|
* @param size size of key to generate
|
|
* @param curveName name of ECC curve on which to generate key
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void makeKeyOnCurve(Rng rng, int size, String curveName)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_make_key_ex(rng, size, curveName.toUpperCase());
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Check correctness of ECC key
|
|
*
|
|
* @throws WolfCryptException if native operation fails or key is
|
|
* incorrect or invalid
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void checkKey()
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_check_key();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import private ECC key
|
|
*
|
|
* @param privKey byte array holding private key
|
|
* @param x963Key byte array holding public key
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void importPrivate(byte[] privKey, byte[] x963Key)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_import_private(privKey, x963Key, null);
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Import private key on specified ECC curve
|
|
*
|
|
* @param privKey byte array holding private key
|
|
* @param x963Key byte array holding public key
|
|
* @param curveName name of ECC curve key is on
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void importPrivateOnCurve(byte[] privKey,
|
|
byte[] x963Key, String curveName)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_import_private(privKey, x963Key, curveName);
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Export private ECC key
|
|
*
|
|
* @return byte array with private key
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] exportPrivate()
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
return wc_ecc_export_private();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import public ECC key in X9.63 format
|
|
*
|
|
* @param key public key in X9.63 format
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void importX963(byte[] key)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_ecc_import_x963(key);
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Export public ECC key in X9.63 format
|
|
*
|
|
* @return public key in X9.63 format
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] exportX963()
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
return wc_ecc_export_x963();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decode/import private ECC key in ASN.1/DER format
|
|
*
|
|
* @param key private key array
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void privateKeyDecode(byte[] key)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_EccPrivateKeyDecode(key);
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Encode/export private ECC key in ASN.1/DER format
|
|
*
|
|
* @return private key byte array
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] privateKeyEncode()
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
return wc_EccKeyToDer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decode/import public ECC key in ASN.1/DER format
|
|
*
|
|
* @param key public key array
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has already been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized void publicKeyDecode(byte[] key)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyExists();
|
|
|
|
synchronized (pointerLock) {
|
|
wc_EccPublicKeyDecode(key);
|
|
}
|
|
state = WolfCryptState.READY;
|
|
}
|
|
|
|
/**
|
|
* Encode/export public ECC key in ASN.1/DER format
|
|
*
|
|
* @return public key byte array
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] publicKeyEncode()
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
return wc_EccPublicKeyToDer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate ECDH shared secret between this object and specified public key
|
|
*
|
|
* @param pubKey public ECC key to use with secret generation
|
|
*
|
|
* @return generated shared secret byte array
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] makeSharedSecret(Ecc pubKey)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
synchronized (rngLock) {
|
|
return wc_ecc_shared_secret(pubKey, this.rng);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate an ECDSA signature.
|
|
*
|
|
* @param hash input hash to be signed
|
|
* @param rng initialized Rng object
|
|
*
|
|
* @return ECDSA signature of input data
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] sign(byte[] hash, Rng rng)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
byte[] signature = null;
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
synchronized (rngLock) {
|
|
signature = wc_ecc_sign_hash(hash, rng);
|
|
}
|
|
}
|
|
|
|
return signature;
|
|
}
|
|
|
|
/**
|
|
* Verify an ECDSA signature.
|
|
*
|
|
* @param hash input hash to verify signature against
|
|
* @param signature input signature to verify
|
|
*
|
|
* @return true if signature verified, otherwise false
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized boolean verify(byte[] hash, byte[] signature)
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
boolean result = false;
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
result = wc_ecc_verify_hash(hash, signature);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get ECC curve size from curve name.
|
|
*
|
|
* Ecc object does not need to be initialized to call this method.
|
|
*
|
|
* @param curveName name of ECC curve
|
|
*
|
|
* @return size of ECC curve
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
*/
|
|
public static int getCurveSizeFromName(String curveName)
|
|
throws WolfCryptException {
|
|
|
|
/* Ecc object doesn't need to be initialied before call */
|
|
return wc_ecc_get_curve_size_from_name(curveName);
|
|
}
|
|
|
|
/**
|
|
* Encode private ECC key in PKCS#8 format.
|
|
*
|
|
* @return encoded private key as byte array
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws IllegalStateException if key has not been set, if object
|
|
* fails to initialize, or if releaseNativeStruct() has been
|
|
* called and object has been released.
|
|
*/
|
|
public synchronized byte[] privateKeyEncodePKCS8()
|
|
throws WolfCryptException, IllegalStateException {
|
|
|
|
checkStateAndInitialize();
|
|
throwIfKeyNotLoaded();
|
|
|
|
synchronized (pointerLock) {
|
|
return wc_ecc_private_key_to_pkcs8();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get ECC curve name from ECParameterSpec
|
|
*
|
|
* @param spec ECParameterSpec to get curve name from
|
|
*
|
|
* @return ECC curve name
|
|
*
|
|
* @throws WolfCryptException if native operation fails
|
|
* @throws InvalidAlgorithmParameterException if spec.getCurve().getField()
|
|
* is not an instance of ECFieldFp
|
|
*/
|
|
public static String getCurveName(ECParameterSpec spec)
|
|
throws WolfCryptException, InvalidAlgorithmParameterException {
|
|
|
|
int curve_id;
|
|
|
|
/* Ecc object doesn't need to be initialied before call */
|
|
if (!(spec.getCurve().getField() instanceof ECFieldFp)) {
|
|
throw new InvalidAlgorithmParameterException(
|
|
"Currently only ECFieldFp fields supported");
|
|
}
|
|
ECFieldFp field = (ECFieldFp)spec.getCurve().getField();
|
|
EllipticCurve curve = spec.getCurve();
|
|
|
|
curve_id = wc_ecc_get_curve_id_from_params(
|
|
field.getFieldSize(),
|
|
field.getP().toByteArray(),
|
|
curve.getA().toByteArray(),
|
|
curve.getB().toByteArray(),
|
|
spec.getOrder().toByteArray(),
|
|
spec.getGenerator().getAffineX().toByteArray(),
|
|
spec.getGenerator().getAffineY().toByteArray(),
|
|
spec.getCofactor());
|
|
|
|
return wc_ecc_get_curve_name_from_id(curve_id);
|
|
}
|
|
}
|
|
|