From 4a41cfc6592afee33ff0037c6e63065b6f2888e3 Mon Sep 17 00:00:00 2001 From: John Gray Date: Fri, 6 Dec 2024 14:31:25 -0500 Subject: [PATCH 1/8] Add context for Java --- src/main/c/Signature.c | 79 ++++++++++++++++ src/main/c/Signature.h | 40 ++++---- .../java/org/openquantumsafe/Signature.java | 94 +++++++++++++++++++ 3 files changed, 189 insertions(+), 24 deletions(-) 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 From d32a034848ffaa4598046b17a3d5d9dce8dc116e Mon Sep 17 00:00:00 2001 From: John Gray Date: Tue, 10 Dec 2024 11:00:26 -0500 Subject: [PATCH 2/8] - Update the release version - Add testing for the context --- RELEASE.md | 4 ++- pom.xml | 2 +- .../java/org/openquantumsafe/SigTest.java | 35 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 4858b5b..81217f5 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -liboqs-java version 0.1.0 +liboqs-java version 0.1.1 ========================= 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.1.1 from December 2024 added support for Signature and Verify API's which accept a Context String. diff --git a/pom.xml b/pom.xml index b80ea94..2d6209f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.openquantumsafe liboqs-java jar - 1.0 + 1.1 liboqs-java: Java wrapper for liboqs liboqs-java offers a Java wrapper providing quantum-resistant cryptographic algorithms via liboqs. diff --git a/src/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index 07fa4fa..a7f6344 100644 --- a/src/test/java/org/openquantumsafe/SigTest.java +++ b/src/test/java/org/openquantumsafe/SigTest.java @@ -53,6 +53,41 @@ public void testAllSigs(String sig_name) { sb.append("\033[0;32m").append("PASSED").append("\033[0m"); System.out.println(sb.toString()); } + + + /** + * Test all enabled Sigs with context (if they don't support the context + * it should fail gracefully) + */ + @ParameterizedTest(name = "Testing {arguments}") + @MethodSource("getEnabledSigsAsStream") + public void testAllSigsWithContext(String sig_name) { + 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); + + byte[] sampleContext = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10}; + + // Generate signer key pair + byte[] signer_public_key = signer.generate_keypair(); + + // Sign the message + byte[] signature = signer.sign(message,sampleContext); + + // Verify the signature + boolean is_valid = verifier.verify(message, signature, sampleContext, 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 From 0f01576b58eac8740feabf28c15c64f6b360edfe Mon Sep 17 00:00:00 2001 From: John Gray Date: Tue, 10 Dec 2024 17:26:20 -0500 Subject: [PATCH 3/8] Update the Java Test file --- .../java/org/openquantumsafe/SigTest.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index a7f6344..fab412f 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 + "]" ); } /** @@ -54,14 +58,14 @@ public void testAllSigs(String sig_name) { System.out.println(sb.toString()); } - /** - * Test all enabled Sigs with context (if they don't support the context - * it should fail gracefully) + * Test Sigs with context. */ @ParameterizedTest(name = "Testing {arguments}") @MethodSource("getEnabledSigsAsStream") - public void testAllSigsWithContext(String sig_name) { + //@MethodSource("getMLDSASigsAsStream") + 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", "")); @@ -69,25 +73,21 @@ public void testAllSigsWithContext(String sig_name) { // Create signer and verifier Signature signer = new Signature(sig_name); Signature verifier = new Signature(sig_name); - - byte[] sampleContext = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10}; - // Generate signer key pair + // Generate signer key pair byte[] signer_public_key = signer.generate_keypair(); // Sign the message - byte[] signature = signer.sign(message,sampleContext); + byte[] signature = signer.sign(message, context); // Verify the signature - boolean is_valid = verifier.verify(message, signature, sampleContext, signer_public_key); - + 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 @@ -104,4 +104,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 getMLDSASigsAsStream() { +// return Arrays.asList( +// "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" +// ).parallelStream(); +// } } From d89df0006fe2867b2a3465cf5ec643ec825c5b9e Mon Sep 17 00:00:00 2001 From: John Gray Date: Wed, 8 Jan 2025 17:10:15 -0500 Subject: [PATCH 4/8] - Update the version to 0.2.0 and make sure that the new Signature Test that makes use of the algorithms only makes use algorithms that support the context (ML-DSA at the moment). --- RELEASE.md | 4 ++-- pom.xml | 2 +- .../java/org/openquantumsafe/SigTest.java | 21 +++++++++---------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 81217f5..98f791b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -liboqs-java version 0.1.1 +liboqs-java version 0.2.0 ========================= About @@ -15,4 +15,4 @@ 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.1.1 from December 2024 added support for Signature and Verify API's which accept a Context String. +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 2d6209f..bad7e78 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.openquantumsafe liboqs-java jar - 1.1 + 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/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index fab412f..d1f283f 100644 --- a/src/test/java/org/openquantumsafe/SigTest.java +++ b/src/test/java/org/openquantumsafe/SigTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; -// import java.util.Arrays; +import java.util.Arrays; import java.util.stream.Stream; public class SigTest { @@ -62,8 +62,7 @@ public void testAllSigs(String sig_name) { * Test Sigs with context. */ @ParameterizedTest(name = "Testing {arguments}") - @MethodSource("getEnabledSigsAsStream") - //@MethodSource("getMLDSASigsAsStream") + @MethodSource("getContextSupportedAlgsAsStream") public void testSigsWithContext(String sig_name) { byte[] context = "01234567890".getBytes(); StringBuilder sb = new StringBuilder(); @@ -104,12 +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 getMLDSASigsAsStream() { -// return Arrays.asList( -// "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" -// ).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(); + } } From d2ac60cf8bea9d35df909765e9aa89408057e8af Mon Sep 17 00:00:00 2001 From: John Gray Date: Fri, 6 Dec 2024 14:31:25 -0500 Subject: [PATCH 5/8] Add context for Java Signed-off-by: John Gray --- src/main/c/Signature.c | 79 ++++++++++++++++ src/main/c/Signature.h | 40 ++++---- .../java/org/openquantumsafe/Signature.java | 94 +++++++++++++++++++ 3 files changed, 189 insertions(+), 24 deletions(-) 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 From 1bd64d04efcef1cf6e5217cb75e9756dbc5518d2 Mon Sep 17 00:00:00 2001 From: John Gray Date: Tue, 10 Dec 2024 11:00:26 -0500 Subject: [PATCH 6/8] - Update the release version - Add testing for the context Signed-off-by: John Gray --- RELEASE.md | 4 ++- pom.xml | 2 +- .../java/org/openquantumsafe/SigTest.java | 35 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 4858b5b..81217f5 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -liboqs-java version 0.1.0 +liboqs-java version 0.1.1 ========================= 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.1.1 from December 2024 added support for Signature and Verify API's which accept a Context String. diff --git a/pom.xml b/pom.xml index b80ea94..2d6209f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.openquantumsafe liboqs-java jar - 1.0 + 1.1 liboqs-java: Java wrapper for liboqs liboqs-java offers a Java wrapper providing quantum-resistant cryptographic algorithms via liboqs. diff --git a/src/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index 07fa4fa..a7f6344 100644 --- a/src/test/java/org/openquantumsafe/SigTest.java +++ b/src/test/java/org/openquantumsafe/SigTest.java @@ -53,6 +53,41 @@ public void testAllSigs(String sig_name) { sb.append("\033[0;32m").append("PASSED").append("\033[0m"); System.out.println(sb.toString()); } + + + /** + * Test all enabled Sigs with context (if they don't support the context + * it should fail gracefully) + */ + @ParameterizedTest(name = "Testing {arguments}") + @MethodSource("getEnabledSigsAsStream") + public void testAllSigsWithContext(String sig_name) { + 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); + + byte[] sampleContext = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10}; + + // Generate signer key pair + byte[] signer_public_key = signer.generate_keypair(); + + // Sign the message + byte[] signature = signer.sign(message,sampleContext); + + // Verify the signature + boolean is_valid = verifier.verify(message, signature, sampleContext, 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 From fa127c474cda939b94a5e514bdeed9a284af95d8 Mon Sep 17 00:00:00 2001 From: John Gray Date: Tue, 10 Dec 2024 17:26:20 -0500 Subject: [PATCH 7/8] Update the Java Test file Signed-off-by: John Gray --- .../java/org/openquantumsafe/SigTest.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index a7f6344..fab412f 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 + "]" ); } /** @@ -54,14 +58,14 @@ public void testAllSigs(String sig_name) { System.out.println(sb.toString()); } - /** - * Test all enabled Sigs with context (if they don't support the context - * it should fail gracefully) + * Test Sigs with context. */ @ParameterizedTest(name = "Testing {arguments}") @MethodSource("getEnabledSigsAsStream") - public void testAllSigsWithContext(String sig_name) { + //@MethodSource("getMLDSASigsAsStream") + 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", "")); @@ -69,25 +73,21 @@ public void testAllSigsWithContext(String sig_name) { // Create signer and verifier Signature signer = new Signature(sig_name); Signature verifier = new Signature(sig_name); - - byte[] sampleContext = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10}; - // Generate signer key pair + // Generate signer key pair byte[] signer_public_key = signer.generate_keypair(); // Sign the message - byte[] signature = signer.sign(message,sampleContext); + byte[] signature = signer.sign(message, context); // Verify the signature - boolean is_valid = verifier.verify(message, signature, sampleContext, signer_public_key); - + 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 @@ -104,4 +104,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 getMLDSASigsAsStream() { +// return Arrays.asList( +// "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" +// ).parallelStream(); +// } } From 0fb6f95fba611d2c60469c54ef7c8ac8ead94c08 Mon Sep 17 00:00:00 2001 From: John Gray Date: Wed, 8 Jan 2025 17:10:15 -0500 Subject: [PATCH 8/8] - Update the version to 0.2.0 and make sure that the new Signature Test that makes use of the algorithms only makes use algorithms that support the context (ML-DSA at the moment). Signed-off-by: John Gray --- RELEASE.md | 4 ++-- pom.xml | 2 +- .../java/org/openquantumsafe/SigTest.java | 21 +++++++++---------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 81217f5..98f791b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -liboqs-java version 0.1.1 +liboqs-java version 0.2.0 ========================= About @@ -15,4 +15,4 @@ 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.1.1 from December 2024 added support for Signature and Verify API's which accept a Context String. +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 2d6209f..bad7e78 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.openquantumsafe liboqs-java jar - 1.1 + 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/test/java/org/openquantumsafe/SigTest.java b/src/test/java/org/openquantumsafe/SigTest.java index fab412f..d1f283f 100644 --- a/src/test/java/org/openquantumsafe/SigTest.java +++ b/src/test/java/org/openquantumsafe/SigTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; -// import java.util.Arrays; +import java.util.Arrays; import java.util.stream.Stream; public class SigTest { @@ -62,8 +62,7 @@ public void testAllSigs(String sig_name) { * Test Sigs with context. */ @ParameterizedTest(name = "Testing {arguments}") - @MethodSource("getEnabledSigsAsStream") - //@MethodSource("getMLDSASigsAsStream") + @MethodSource("getContextSupportedAlgsAsStream") public void testSigsWithContext(String sig_name) { byte[] context = "01234567890".getBytes(); StringBuilder sb = new StringBuilder(); @@ -104,12 +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 getMLDSASigsAsStream() { -// return Arrays.asList( -// "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" -// ).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(); + } }