diff --git a/README.md b/README.md index f89f902..eff9af1 100644 --- a/README.md +++ b/README.md @@ -147,19 +147,20 @@ KEM Details: Length secret key (bytes): 1632 Length ciphertext (bytes): 768 Length shared secret (bytes): 32 + Length keypair seed (bytes): 64 Client public key: -A8 37 25 CA 79 A5 55 42 ... AF 43 3A 54 6C 3C 34 30 +A4 E7 5D DB AB 9D FA 13 ... 32 9C 08 3F 71 D6 BA 41 It took 1 millisecs to generate the key pair. It took 0 millisecs to encapsulate the secret. It took 0 millisecs to decapsulate the secret. Client shared secret: -7D 3B BB C7 29 45 4B 2F ... 58 87 1D BB BD 35 9C 79 +5C BE 27 50 C8 7E 61 36 ... 07 60 EA 4C 3E 25 90 3F Server shared secret: -7D 3B BB C7 29 45 4B 2F ... 58 87 1D BB BD 35 9C 79 +5C BE 27 50 C8 7E 61 36 ... 07 60 EA 4C 3E 25 90 3F Shared secrets coincide? true ``` @@ -173,10 +174,10 @@ java -cp target/liboqs-java.jar:examples/ SigExample ``` Supported signatures: -Dilithium2 Dilithium3 Dilithium5 ML-DSA-44 ML-DSA-65 ML-DSA-87 Falcon-512 Falcon-1024 Falcon-padded-512 Falcon-padded-1024 SPHINCS+-SHA2-128f-simple SPHINCS+-SHA2-128s-simple SPHINCS+-SHA2-192f-simple SPHINCS+-SHA2-192s-simple SPHINCS+-SHA2-256f-simple SPHINCS+-SHA2-256s-simple SPHINCS+-SHAKE-128f-simple SPHINCS+-SHAKE-128s-simple SPHINCS+-SHAKE-192f-simple SPHINCS+-SHAKE-192s-simple SPHINCS+-SHAKE-256f-simple SPHINCS+-SHAKE-256s-simple MAYO-1 MAYO-2 MAYO-3 MAYO-5 cross-rsdp-128-balanced cross-rsdp-128-fast cross-rsdp-128-small cross-rsdp-192-balanced cross-rsdp-192-fast cross-rsdp-192-small cross-rsdp-256-balanced cross-rsdp-256-fast cross-rsdp-256-small cross-rsdpg-128-balanced cross-rsdpg-128-fast cross-rsdpg-128-small cross-rsdpg-192-balanced cross-rsdpg-192-fast cross-rsdpg-192-small cross-rsdpg-256-balanced cross-rsdpg-256-fast cross-rsdpg-256-small +Dilithium2 Dilithium3 Dilithium5 ML-DSA-44 ML-DSA-65 ML-DSA-87 Falcon-512 Falcon-1024 Falcon-padded-512 Falcon-padded-1024 SPHINCS+-SHA2-128f-simple SPHINCS+-SHA2-128s-simple SPHINCS+-SHA2-192f-simple SPHINCS+-SHA2-192s-simple SPHINCS+-SHA2-256f-simple SPHINCS+-SHA2-256s-simple SPHINCS+-SHAKE-128f-simple SPHINCS+-SHAKE-128s-simple SPHINCS+-SHAKE-192f-simple SPHINCS+-SHAKE-192s-simple SPHINCS+-SHAKE-256f-simple SPHINCS+-SHAKE-256s-simple MAYO-1 MAYO-2 MAYO-3 MAYO-5 cross-rsdp-128-balanced cross-rsdp-128-fast cross-rsdp-128-small cross-rsdp-192-balanced cross-rsdp-192-fast cross-rsdp-192-small cross-rsdp-256-balanced cross-rsdp-256-fast cross-rsdp-256-small cross-rsdpg-128-balanced cross-rsdpg-128-fast cross-rsdpg-128-small cross-rsdpg-192-balanced cross-rsdpg-192-fast cross-rsdpg-192-small cross-rsdpg-256-balanced cross-rsdpg-256-fast cross-rsdpg-256-small OV-Is OV-Ip OV-III OV-V OV-Is-pkc OV-Ip-pkc OV-III-pkc OV-V-pkc OV-Is-pkc-skc OV-Ip-pkc-skc OV-III-pkc-skc OV-V-pkc-skc Enabled signatures: -Dilithium2 Dilithium3 Dilithium5 ML-DSA-44 ML-DSA-65 ML-DSA-87 Falcon-512 Falcon-1024 Falcon-padded-512 Falcon-padded-1024 SPHINCS+-SHA2-128f-simple SPHINCS+-SHA2-128s-simple SPHINCS+-SHA2-192f-simple SPHINCS+-SHA2-192s-simple SPHINCS+-SHA2-256f-simple SPHINCS+-SHA2-256s-simple SPHINCS+-SHAKE-128f-simple SPHINCS+-SHAKE-128s-simple SPHINCS+-SHAKE-192f-simple SPHINCS+-SHAKE-192s-simple SPHINCS+-SHAKE-256f-simple SPHINCS+-SHAKE-256s-simple MAYO-1 MAYO-2 MAYO-3 MAYO-5 cross-rsdp-128-balanced cross-rsdp-128-fast cross-rsdp-128-small cross-rsdp-192-balanced cross-rsdp-192-fast cross-rsdp-192-small cross-rsdp-256-balanced cross-rsdp-256-fast cross-rsdp-256-small cross-rsdpg-128-balanced cross-rsdpg-128-fast cross-rsdpg-128-small cross-rsdpg-192-balanced cross-rsdpg-192-fast cross-rsdpg-192-small cross-rsdpg-256-balanced cross-rsdpg-256-fast cross-rsdpg-256-small +Dilithium2 Dilithium3 Dilithium5 ML-DSA-44 ML-DSA-65 ML-DSA-87 Falcon-512 Falcon-1024 Falcon-padded-512 Falcon-padded-1024 SPHINCS+-SHA2-128f-simple SPHINCS+-SHA2-128s-simple SPHINCS+-SHA2-192f-simple SPHINCS+-SHA2-192s-simple SPHINCS+-SHA2-256f-simple SPHINCS+-SHA2-256s-simple SPHINCS+-SHAKE-128f-simple SPHINCS+-SHAKE-128s-simple SPHINCS+-SHAKE-192f-simple SPHINCS+-SHAKE-192s-simple SPHINCS+-SHAKE-256f-simple SPHINCS+-SHAKE-256s-simple MAYO-1 MAYO-2 MAYO-3 MAYO-5 cross-rsdp-128-balanced cross-rsdp-128-fast cross-rsdp-128-small cross-rsdp-192-balanced cross-rsdp-192-fast cross-rsdp-192-small cross-rsdp-256-balanced cross-rsdp-256-fast cross-rsdp-256-small cross-rsdpg-128-balanced cross-rsdpg-128-fast cross-rsdpg-128-small cross-rsdpg-192-balanced cross-rsdpg-192-fast cross-rsdpg-192-small cross-rsdpg-256-balanced cross-rsdpg-256-fast cross-rsdpg-256-small OV-Is OV-Ip OV-III OV-V OV-Is-pkc OV-Ip-pkc OV-III-pkc OV-V-pkc OV-Is-pkc-skc OV-Ip-pkc-skc OV-III-pkc-skc OV-V-pkc-skc Signature Details: Name: ML-DSA-44 @@ -188,14 +189,14 @@ Signature Details: Maximum length signature (bytes): 2420 Signer public key: -CB CB 70 FF 1E B3 BA 26 ... A7 CF 7C 70 89 A1 1A 40 +2F F1 7A 8F FF EA 04 AA ... FD 51 A2 A0 80 5C 61 2B It took 1 millisecs to generate the key pair. -It took 1 millisecs to sign the message. +It took 0 millisecs to sign the message. It took 0 millisecs to verify the signature. Signature: -ED 6F 67 B6 2E C9 31 FC ... 00 00 00 00 0F 21 2A 38 +C0 41 9D 4D A9 B1 5F 4C ... 00 00 00 00 0A 20 2E 41 Valid signature? true ``` diff --git a/src/main/c/KeyEncapsulation.c b/src/main/c/KeyEncapsulation.c index 32fe42b..83833b8 100644 --- a/src/main/c/KeyEncapsulation.c +++ b/src/main/c/KeyEncapsulation.c @@ -85,6 +85,10 @@ JNIEXPORT jobject JNICALL Java_org_openquantumsafe_KeyEncapsulation_get_1KEM_1de jfieldID _length_shared_secret = (*env)->GetFieldID(env, cls, "length_shared_secret", "J"); (*env)->SetLongField(env, _nativeKED, _length_shared_secret, (jlong) kem->length_shared_secret); + // long length_keypair_seed; + jfieldID _length_keypair_seed = (*env)->GetFieldID(env, cls, "length_keypair_seed", "J"); + (*env)->SetLongField(env, _nativeKED, _length_keypair_seed, (jlong) kem->length_keypair_seed); + return _nativeKED; } @@ -110,6 +114,30 @@ JNIEXPORT jint JNICALL Java_org_openquantumsafe_KeyEncapsulation_generate_1keypa return (rv_ == OQS_SUCCESS) ? 0 : -1; } +/* + * Class: org_openquantumsafe_KeyEncapsulation + * Method: generate_keypair + * Signature: ([B[B)I + */ +JNIEXPORT jint JNICALL Java_org_openquantumsafe_KeyEncapsulation_generate_1keypair_1derand + (JNIEnv *env, jobject obj, jbyteArray jpublic_key, jbyteArray jsecret_key, jbyteArray jseed) +{ + jbyte *public_key_native = (*env)->GetByteArrayElements(env, jpublic_key, 0); + jbyte *secret_key_native = (*env)->GetByteArrayElements(env, jsecret_key, 0); + jbyte *seed_native = (*env)->GetByteArrayElements(env, jseed, 0); + + // Get pointer to KEM + OQS_KEM *kem = (OQS_KEM *) getHandle(env, obj, "native_kem_handle_"); + + // Invoke liboqs KEM keypair generation function + OQS_STATUS rv_ = OQS_KEM_keypair_derand(kem, (uint8_t*) public_key_native, (uint8_t*) secret_key_native, (uint8_t*) seed_native); + + (*env)->ReleaseByteArrayElements(env, jpublic_key, public_key_native, 0); + (*env)->ReleaseByteArrayElements(env, jsecret_key, secret_key_native, 0); + (*env)->ReleaseByteArrayElements(env, jseed, seed_native, JNI_ABORT); + return (rv_ == OQS_SUCCESS) ? 0 : -1; +} + /* * Class: org_openquantumsafe_KeyEncapsulation * Method: encap_secret diff --git a/src/main/java/org/openquantumsafe/KeyEncapsulation.java b/src/main/java/org/openquantumsafe/KeyEncapsulation.java index 3980ff5..2a88327 100644 --- a/src/main/java/org/openquantumsafe/KeyEncapsulation.java +++ b/src/main/java/org/openquantumsafe/KeyEncapsulation.java @@ -20,6 +20,7 @@ class KeyEncapsulationDetails { long length_secret_key; long length_ciphertext; long length_shared_secret; + long length_keypair_seed; /** * \brief Print KEM algorithm details @@ -33,7 +34,9 @@ void printKeyEncapsulation() { "\n Length public key (bytes): " + this.length_public_key + "\n Length secret key (bytes): " + this.length_secret_key + "\n Length ciphertext (bytes): " + this.length_ciphertext + - "\n Length shared secret (bytes): " + this.length_shared_secret + "\n Length shared secret (bytes): " + this.length_shared_secret + + "\n Length keypair seed (bytes): " + + ((this.length_keypair_seed > 0) ? this.length_keypair_seed : "N/A") ); } @@ -114,6 +117,18 @@ public KeyEncapsulation(String alg_name, byte[] secret_key) */ private native int generate_keypair(byte[] public_key, byte[] secret_key); + /** + * \brief Wrapper for OQS_API OQS_STATUS OQS_KEM_keypair_derand(const OQS_KEM *kem, + * uint8_t *public_key, uint8_t *secret_key, + * const uint8_t *seed); + * \param Public key + * \param Secret key + * \param Seed + * \return Status + */ + private native int generate_keypair_derand(byte[] public_key, + byte[] secret_key, byte[] seed); + /** * \brief Wrapper for OQS_API OQS_STATUS OQS_KEM_encaps(const OQS_KEM *kem, * uint8_t *ciphertext, @@ -159,6 +174,27 @@ public byte[] generate_keypair() throws RuntimeException { return this.public_key_; } + /** + * \brief Invoke native generate_keypair_derand method using the PK and SK lengths + * from alg_details_. Check return value and if != 0 throw Exception. + */ + public byte[] generate_keypair(byte[] seed) throws RuntimeException { + if (seed.length != alg_details_.length_keypair_seed) { + throw new RuntimeException("Incorrect seed length"); + } + + int rv_ = generate_keypair_derand(this.public_key_, this.secret_key_, seed); + if (rv_ != 0) throw new RuntimeException("Cannot generate keypair from seed"); + return this.public_key_; + } + + /** + * \brief Return seed length + */ + public long get_keypair_seed_length() { + return alg_details_.length_keypair_seed; + } + /** * \brief Return public key */ diff --git a/src/test/java/org/openquantumsafe/KEMTest.java b/src/test/java/org/openquantumsafe/KEMTest.java index 5e1511d..1a5e682 100644 --- a/src/test/java/org/openquantumsafe/KEMTest.java +++ b/src/test/java/org/openquantumsafe/KEMTest.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import java.util.ArrayList; +import java.util.Arrays; import java.util.stream.Stream; public class KEMTest { @@ -56,6 +57,43 @@ public void testAllKEMs(String kem_name) { System.out.println(sb.toString()); } + /** + * Test KEMs with derandomized keypair generation. + */ + @ParameterizedTest(name = "Testing {arguments}") + @MethodSource("getDerandSupportedKEMsAsStream") + public void testKEMsWithDerand(String kem_name) { + StringBuilder sb = new StringBuilder(); + sb.append(kem_name); + sb.append(" (derand)"); + sb.append(String.format("%1$" + (40 - kem_name.length() - 9) + "s", "")); + + // Create client and server + KeyEncapsulation client = new KeyEncapsulation(kem_name); + KeyEncapsulation server = new KeyEncapsulation(kem_name); + + // Generate seed + byte[] seed = Rand.randombytes(client.get_keypair_seed_length()); + + // Generate client key pair + byte[] client_public_key = client.generate_keypair(seed); + + // Server: encapsulate secret with client's public key + Pair server_pair = server.encap_secret(client_public_key); + byte[] ciphertext = server_pair.getLeft(); + byte[] shared_secret_server = server_pair.getRight(); + + // Client: decapsulate + byte[] shared_secret_client = client.decap_secret(ciphertext); + + // Check if equal + assertArrayEquals(shared_secret_client, shared_secret_server, kem_name); + + // If successful print KEM 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 */ @@ -71,4 +109,12 @@ private static Stream getEnabledKEMsAsStream() { return enabled_kems.parallelStream(); } + /** + * Method to convert the list of derand-supported KEMs to a stream for input to testAllSigs + */ + private static Stream getDerandSupportedKEMsAsStream() { + return Arrays.asList( + "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024" + ).parallelStream(); + } }