diff --git a/RELEASE.md b/RELEASE.md index 4858b5b..98f791b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -liboqs-java version 0.1.0 +liboqs-java version 0.2.0 ========================= About @@ -14,3 +14,5 @@ Release notes ============= The initial release of liboqs-java was released on July 8, 2020. Its release page on GitHub is https://github.com/open-quantum-safe/liboqs-java/releases/tag/0.1.0. + +Release 0.2.0 from January 2025 added support for Signature and Verify API's which accept a Context String. diff --git a/pom.xml b/pom.xml index b80ea94..bad7e78 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.openquantumsafe liboqs-java jar - 1.0 + 2.0 liboqs-java: Java wrapper for liboqs liboqs-java offers a Java wrapper providing quantum-resistant cryptographic algorithms via liboqs. diff --git a/src/main/c/Signature.c b/src/main/c/Signature.c index e2203c1..00efd07 100644 --- a/src/main/c/Signature.c +++ b/src/main/c/Signature.c @@ -122,6 +122,7 @@ JNIEXPORT jint JNICALL Java_org_openquantumsafe_Signature_sign OQS_SIG *sig = (OQS_SIG *) getHandle(env, obj, "native_sig_handle_"); size_t len_sig; + OQS_STATUS rv_ = OQS_SIG_sign(sig, (uint8_t*)signature_native, &len_sig, (uint8_t*)message_native, message_len, (uint8_t*)secret_key_native); @@ -173,3 +174,81 @@ JNIEXPORT jboolean JNICALL Java_org_openquantumsafe_Signature_verify return (rv_ == OQS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } + +/* + * Class: org_openquantumsafe_Signature + * Method: sign_with_ctx_str + * Signature: ([BLjava/lang/Long;[BJ[B)I + */ +JNIEXPORT jint JNICALL Java_org_openquantumsafe_Signature_sign_1with_1ctx_1str + (JNIEnv * env, jobject obj, jbyteArray jsignature, jobject sig_len_obj, + jbyteArray jmessage, jlong message_len, jbyteArray jctx, jlong ctx_len, + jbyteArray jsecret_key) +{ + // Convert to jbyte arrays + jbyte *signature_native = (*env)->GetByteArrayElements(env, jsignature, 0); + jbyte *message_native = (*env)->GetByteArrayElements(env, jmessage, 0); + jbyte *ctx_native = (*env)->GetByteArrayElements(env, jctx, 0); + jbyte *secret_key_native = (*env)->GetByteArrayElements(env, jsecret_key, 0); + + OQS_SIG *sig = (OQS_SIG *) getHandle(env, obj, "native_sig_handle_"); + size_t len_sig; + OQS_STATUS rv_ = OQS_SIG_sign_with_ctx_str(sig, (uint8_t*)signature_native, &len_sig, + (uint8_t*)message_native, message_len, + (uint8_t*)ctx_native, ctx_len, + (uint8_t*)secret_key_native); + + // fill java signature bytes + (*env)->SetByteArrayRegion(env, jsignature, 0, len_sig, (jbyte*) signature_native); + + // fill java object signature length + jfieldID value_fid = (*env)->GetFieldID(env, + (*env)->GetObjectClass(env, sig_len_obj), + "value", "Ljava/lang/Object;"); + jclass cls = (*env)->FindClass(env, "java/lang/Long"); + jobject jlong_obj = (*env)->NewObject(env, cls, + (*env)->GetMethodID(env, cls, "", "(J)V"), + (jlong) len_sig); + (*env)->SetObjectField(env, sig_len_obj, value_fid, jlong_obj); + + // Release C memory + (*env)->ReleaseByteArrayElements(env, jsignature, signature_native, 0); + (*env)->ReleaseByteArrayElements(env, jmessage, message_native, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, jctx, ctx_native, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, jsecret_key, secret_key_native, JNI_ABORT); + + return (rv_ == OQS_SUCCESS) ? 0 : -1; +} + +/* + * Class: org_openquantumsafe_Signature + * Method: verify_with_ctx_str + * Signature: ([BJ[BJ[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_openquantumsafe_Signature_verify_1with_1ctx_1str + (JNIEnv *env, jobject obj, jbyteArray jmessage, jlong message_len, + jbyteArray jsignature, jlong signature_len, jbyteArray jctx, jlong ctx_len, + jbyteArray jpublic_key) +{ + // Convert to jbyte arrays + jbyte *message_native = (*env)->GetByteArrayElements(env, jmessage, 0); + jbyte *signature_native = (*env)->GetByteArrayElements(env, jsignature, 0); + jbyte *ctx_native = (*env)->GetByteArrayElements(env, jctx, 0); + jbyte *public_key_native = (*env)->GetByteArrayElements(env, jpublic_key, 0); + + OQS_SIG *sig = (OQS_SIG *) getHandle(env, obj, "native_sig_handle_"); + OQS_STATUS rv_ = OQS_SIG_verify_with_ctx_str(sig, (uint8_t*) message_native, message_len, + (uint8_t*) signature_native, signature_len, + (uint8_t*) ctx_native, ctx_len, + (uint8_t*) public_key_native); + + // Release C memory + (*env)->ReleaseByteArrayElements(env, jsignature, signature_native, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, jmessage, message_native, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, jctx, ctx_native, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, jpublic_key, public_key_native, JNI_ABORT); + + return (rv_ == OQS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + + diff --git a/src/main/c/Signature.h b/src/main/c/Signature.h index 611c4f8..9a36969 100644 --- a/src/main/c/Signature.h +++ b/src/main/c/Signature.h @@ -41,43 +41,35 @@ JNIEXPORT jint JNICALL Java_org_openquantumsafe_Signature_generate_1keypair /* * Class: org_openquantumsafe_Signature - * Method: import_secret_key - * Signature: ([B)V - */ -JNIEXPORT void JNICALL Java_org_openquantumsafe_Signature_import_1secret_1key - (JNIEnv *, jobject, jbyteArray); - -/* - * Class: org_openquantumsafe_Signature - * Method: export_public_key - * Signature: ([B)V + * Method: sign + * Signature: ([BLorg/openquantumsafe/Signature/Mutable;[BJ[B)I */ -JNIEXPORT void JNICALL Java_org_openquantumsafe_Signature_export_1public_1key - (JNIEnv *, jobject, jbyteArray); +JNIEXPORT jint JNICALL Java_org_openquantumsafe_Signature_sign + (JNIEnv *, jobject, jbyteArray, jobject, jbyteArray, jlong, jbyteArray); /* * Class: org_openquantumsafe_Signature - * Method: export_secret_key - * Signature: ([B)V + * Method: verify + * Signature: ([BJ[BJ[B)Z */ -JNIEXPORT void JNICALL Java_org_openquantumsafe_Signature_export_1secret_1key - (JNIEnv *, jobject, jbyteArray); +JNIEXPORT jboolean JNICALL Java_org_openquantumsafe_Signature_verify + (JNIEnv *, jobject, jbyteArray, jlong, jbyteArray, jlong, jbyteArray); /* * Class: org_openquantumsafe_Signature - * Method: sign - * Signature: ([BLjava/lang/Long;[BJ[B)I + * Method: sign_with_ctx_str + * Signature: ([BLorg/openquantumsafe/Signature/Mutable;[BJ[BJ[B)I */ -JNIEXPORT jint JNICALL Java_org_openquantumsafe_Signature_sign - (JNIEnv *, jobject, jbyteArray, jobject, jbyteArray, jlong, jbyteArray); +JNIEXPORT jint JNICALL Java_org_openquantumsafe_Signature_sign_1with_1ctx_1str + (JNIEnv *, jobject, jbyteArray, jobject, jbyteArray, jlong, jbyteArray, jlong, jbyteArray); /* * Class: org_openquantumsafe_Signature - * Method: verify - * Signature: ([BJ[BJ[B)Z + * Method: verify_with_ctx_str + * Signature: ([BJ[BJ[BJ[B)Z */ -JNIEXPORT jboolean JNICALL Java_org_openquantumsafe_Signature_verify - (JNIEnv *, jobject, jbyteArray, jlong, jbyteArray, jlong, jbyteArray); +JNIEXPORT jboolean JNICALL Java_org_openquantumsafe_Signature_verify_1with_1ctx_1str + (JNIEnv *, jobject, jbyteArray, jlong, jbyteArray, jlong, jbyteArray, jlong, jbyteArray); #ifdef __cplusplus } diff --git a/src/main/java/org/openquantumsafe/Signature.java b/src/main/java/org/openquantumsafe/Signature.java index e4128e8..2055877 100644 --- a/src/main/java/org/openquantumsafe/Signature.java +++ b/src/main/java/org/openquantumsafe/Signature.java @@ -146,6 +146,51 @@ private native int sign(byte[] signature, Mutable signature_len_ret, private native boolean verify(byte[] message, long message_len, byte[] signature, long signature_len, byte[] public_key); + + /** + * \brief Wrapper for OQS_API OQS_STATUS OQS_SIG_sign_with_ctx_str(const OQS_SIG *sig, + * uint8_t *signature, + * size_t *signature_len, + * const uint8_t *message, + * size_t message_len, + * const uint8_t *ctx, + * size_t ctx_len, + * const uint8_t *secret_key); + * \param signature + * \param signature_len_ret + * \param message + * \param message_len + * \param ctx + * \param ctx_len + * \param secret_key + * \return Status + */ + private native int sign_with_ctx_str(byte[] signature, Mutable signature_len_ret, + byte[] message, long message_len, byte[] ctx, long ctx_len, + byte[] secret_key); + + /** + * \brief Wrapper for OQS_API OQS_STATUS OQS_SIG_verify_with_ctx_str(const OQS_SIG *sig, + * const uint8_t *message, + * size_t message_len, + * const uint8_t *signature, + * size_t signature_len, + * const uint8_t *ctx, + * size_t ctx_len, + * const uint8_t *public_key); + * \param message + * \param message_len + * \param signature + * \param signature_len + * \param ctx + * \param ctx_len + * \param public_key + * \return True if the signature is valid, false otherwise + */ + private native boolean verify_with_ctx_str(byte[] message, long message_len, + byte[] signature, long signature_len, + byte[] ctx, long ctx_len, + byte[] public_key); /** * \brief Invoke native free_sig @@ -220,6 +265,55 @@ public boolean verify(byte[] message, byte[] signature, byte[] public_key) return verify(message, message.length, signature, signature.length, public_key); } + + /** + * \brief Invoke native sign method + * \param message + * \param ctx + * \return signature + */ + public byte[] sign(byte[] message, byte[] ctx) throws RuntimeException { + if (this.secret_key_.length != alg_details_.length_secret_key) { + throw new RuntimeException("Incorrect secret key length, " + + "make sure you specify one in the " + + "constructor or run generate_keypair()"); + } + byte[] signature = new byte[(int) alg_details_.max_length_signature]; + Mutable signature_len_ret = new Mutable<>(); + int ctx_len = (ctx == null) ? 0 : ctx.length; + int rv_= sign_with_ctx_str(signature, signature_len_ret, + message, message.length, + ctx, ctx_len, + this.secret_key_); + long actual_signature_len = signature_len_ret.value; + byte[] actual_signature = new byte[(int) actual_signature_len]; + System.arraycopy(signature, 0, + actual_signature, 0, (int) actual_signature_len); + if (rv_ != 0) throw new RuntimeException("Cannot sign message"); + return actual_signature; + } + + /** + * \brief Invoke native verify method + * \param message + * \param signature + * \param ctx + * \param public_key + * \return True if the signature is valid, false otherwise + */ + public boolean verify(byte[] message, byte[] signature, byte[] ctx, byte[] public_key) + throws RuntimeException { + if (public_key.length != alg_details_.length_public_key) { + throw new RuntimeException("Incorrect public key length"); + } + if (signature.length > alg_details_.max_length_signature) { + throw new RuntimeException("Incorrect signature length"); + } + + int ctx_len = (ctx == null) ? 0 : ctx.length; + + return verify_with_ctx_str(message, message.length, signature, signature.length, ctx, ctx_len, public_key); + } /** * \brief Print Signature. If a SignatureDetails object is not diff --git a/src/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index 07fa4fa..d1f283f 100644 --- a/src/test/java/org/openquantumsafe/SigTest.java +++ b/src/test/java/org/openquantumsafe/SigTest.java @@ -5,9 +5,12 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; + +// import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; +import java.util.Arrays; import java.util.stream.Stream; public class SigTest { @@ -22,6 +25,7 @@ public class SigTest { public static void init(){ System.out.println("Initialize list of enabled Signatures"); enabled_sigs = Sigs.get_enabled_sigs(); + System.out.println("Enabled signatures: [" + enabled_sigs + "]" ); } /** @@ -53,6 +57,36 @@ public void testAllSigs(String sig_name) { sb.append("\033[0;32m").append("PASSED").append("\033[0m"); System.out.println(sb.toString()); } + + /** + * Test Sigs with context. + */ + @ParameterizedTest(name = "Testing {arguments}") + @MethodSource("getContextSupportedAlgsAsStream") + public void testSigsWithContext(String sig_name) { + byte[] context = "01234567890".getBytes(); + StringBuilder sb = new StringBuilder(); + sb.append(sig_name); + sb.append(String.format("%1$" + (40 - sig_name.length()) + "s", "")); + + // Create signer and verifier + Signature signer = new Signature(sig_name); + Signature verifier = new Signature(sig_name); + + // Generate signer key pair + byte[] signer_public_key = signer.generate_keypair(); + + // Sign the message + byte[] signature = signer.sign(message, context); + + // Verify the signature + boolean is_valid = verifier.verify(message, signature, context, signer_public_key); + assertTrue(is_valid, sig_name); + + // If successful print Sig name, otherwise an exception will be thrown + sb.append("\033[0;32m").append("PASSED").append("\033[0m"); + System.out.println(sb.toString()); + } /** * Test the MechanismNotSupported Exception @@ -69,4 +103,12 @@ private static Stream getEnabledSigsAsStream() { return enabled_sigs.parallelStream(); } + /** + * Method to convert the list of ML-DSA Sigs to a stream for input to testAllSigs + */ + private static Stream getContextSupportedAlgsAsStream() { + return Arrays.asList( + "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" + ).parallelStream(); + } }