Skip to content

Add context for Java #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
liboqs-java version 0.1.0
liboqs-java version 0.2.0
=========================

About
Expand All @@ -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.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>org.openquantumsafe</groupId>
<artifactId>liboqs-java</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<version>2.0</version>
<name>liboqs-java: Java wrapper for liboqs</name>
<description>liboqs-java offers a Java wrapper providing quantum-resistant cryptographic algorithms via liboqs.</description>

Expand Down
79 changes: 79 additions & 0 deletions src/main/c/Signature.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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, "<init>", "(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;
}


40 changes: 16 additions & 24 deletions src/main/c/Signature.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 94 additions & 0 deletions src/main/java/org/openquantumsafe/Signature.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,51 @@ private native int sign(byte[] signature, Mutable<Long> 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<Long> 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
Expand Down Expand Up @@ -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<Long> 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
Expand Down
42 changes: 42 additions & 0 deletions src/test/java/org/openquantumsafe/SigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 + "]" );
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -69,4 +103,12 @@ private static Stream<String> getEnabledSigsAsStream() {
return enabled_sigs.parallelStream();
}

/**
* Method to convert the list of ML-DSA Sigs to a stream for input to testAllSigs
*/
private static Stream<String> getContextSupportedAlgsAsStream() {
return Arrays.asList(
"ML-DSA-44", "ML-DSA-65", "ML-DSA-87"
).parallelStream();
}
}