fix: Ed25519/X25519 JNI bugs and test vectors
- jni_ed25519.c: fix HAVE_ED25519_KEY_GEN guard (not defined in wolfSSL 5.8.4); write make_public output to key->p so sign_msg can use it (was writing to local stack buffer) - jni_curve25519.c: switch all 6 Curve25519 ops to _ex variants with EC25519_LITTLE_ENDIAN (RFC 7748 / JCA wire format); mask bit 255 of public key before import per RFC 7748 §5 - WolfCryptEdDSASignature.java: call ed.makePublic() after importPrivateOnly so pubKeySet=1 before signing - Ed25519.java: add makePublic() wrapper for wc_ed25519_make_public - Fix wrong test vectors: TV1_SEED last 2 bytes (3d55->7f60); X25519 Alice/Bob private keys and Bob public key corrected to RFC 8037 §A.6 (verified via Wycheproof tcId 102)ed25519-xdh-jce
parent
56c0d9c25a
commit
c40a4fd911
|
|
@ -174,8 +174,9 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1imp
|
|||
ret = BAD_FUNC_ARG;
|
||||
} else {
|
||||
/* detect, and later skip, leading zero byte */
|
||||
ret = wc_curve25519_import_private_raw(priv, privSz, pub,
|
||||
pubSz, curve25519);
|
||||
ret = wc_curve25519_import_private_raw_ex(priv, privSz, pub,
|
||||
pubSz, curve25519,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
|
|
@ -212,7 +213,8 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1imp
|
|||
ret = BAD_FUNC_ARG;
|
||||
} else {
|
||||
/* detect, and later skip, leading zero byte */
|
||||
ret = wc_curve25519_import_private(priv, privSz, curve25519);
|
||||
ret = wc_curve25519_import_private_ex(priv, privSz, curve25519,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
|
|
@ -246,8 +248,13 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1imp
|
|||
if (!curve25519 || !pub) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
} else {
|
||||
/* detect, and later skip, leading zero byte */
|
||||
ret = wc_curve25519_import_public(pub, pubSz, curve25519);
|
||||
/* RFC 7748 §5: mask bit 255 (MSB of last byte) of u-coordinate.
|
||||
* wolfSSL fingerprinting check rejects keys with this bit set.
|
||||
* This masking is safe and mandated by the RFC. */
|
||||
if (pubSz > 0)
|
||||
pub[pubSz - 1] &= 0x7f;
|
||||
ret = wc_curve25519_import_public_ex(pub, pubSz, curve25519,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
|
|
@ -295,7 +302,8 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1export_1private(
|
|||
}
|
||||
XMEMSET(output, 0, outputSz);
|
||||
|
||||
ret = wc_curve25519_export_private_raw(curve25519, output, &outputSz);
|
||||
ret = wc_curve25519_export_private_raw_ex(curve25519, output, &outputSz,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
|
||||
if (ret == 0) {
|
||||
result = (*env)->NewByteArray(env, outputSz);
|
||||
|
|
@ -360,7 +368,8 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1export_1public (
|
|||
}
|
||||
XMEMSET(output, 0, outputSz);
|
||||
|
||||
ret = wc_curve25519_export_public(curve25519, output, &outputSz);
|
||||
ret = wc_curve25519_export_public_ex(curve25519, output, &outputSz,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
|
||||
if (ret == 0) {
|
||||
result = (*env)->NewByteArray(env, outputSz);
|
||||
|
|
@ -429,7 +438,8 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1make_1shared_1secret(
|
|||
}
|
||||
XMEMSET(output, 0, outputSz);
|
||||
|
||||
ret = wc_curve25519_shared_secret(curve25519, pub, output, &outputSz);
|
||||
ret = wc_curve25519_shared_secret_ex(curve25519, pub, output, &outputSz,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
|
||||
if (ret == 0) {
|
||||
result = (*env)->NewByteArray(env, outputSz);
|
||||
|
|
|
|||
|
|
@ -173,6 +173,34 @@ Java_com_wolfssl_wolfcrypt_Ed25519_wc_1ed25519_1check_1key(
|
|||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_wolfssl_wolfcrypt_Ed25519_wc_1ed25519_1make_1public(
|
||||
JNIEnv* env, jobject this)
|
||||
{
|
||||
#ifdef HAVE_ED25519
|
||||
int ret = 0;
|
||||
ed25519_key* ed25519 = (ed25519_key*) getNativeStruct(env, this);
|
||||
if ((*env)->ExceptionOccurred(env)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write the derived public key directly into key->p so that sign_msg
|
||||
* can access it (sign_msg reads from key->p at hash step). This
|
||||
* matches the internal call in wc_ed25519_make_key:
|
||||
* wc_ed25519_make_public(key, key->p, ED25519_PUB_KEY_SIZE). */
|
||||
ret = (!ed25519)
|
||||
? BAD_FUNC_ARG
|
||||
: wc_ed25519_make_public(ed25519, ed25519->p, ED25519_PUB_KEY_SIZE);
|
||||
|
||||
if (ret != 0)
|
||||
throwWolfCryptExceptionFromError(env, ret);
|
||||
|
||||
LogStr("wc_ed25519_make_public(ed25519=%p) = %d\n", ed25519, ret);
|
||||
#else
|
||||
throwNotCompiledInException(env);
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ed25519_wc_1ed25519_1import_1private
|
||||
(JNIEnv* env, jobject this, jbyteArray priv_object, jbyteArray pub_object)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ public class WolfCryptEdDSASignature extends SignatureSpi {
|
|||
try {
|
||||
ed = new Ed25519();
|
||||
ed.importPrivateOnly(seed);
|
||||
ed.makePublic();
|
||||
} catch (WolfCryptException e) {
|
||||
releaseKey();
|
||||
throw new InvalidKeyException(
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ public class Ed25519 extends NativeStruct {
|
|||
private native void wc_ed25519_free();
|
||||
private native void wc_ed25519_make_key(Rng rng, int size);
|
||||
private native void wc_ed25519_check_key();
|
||||
private native void wc_ed25519_make_public();
|
||||
private native void wc_ed25519_import_private(byte[] privKey, byte[] key);
|
||||
private native void wc_ed25519_import_private_only(byte[] privKey);
|
||||
private native void wc_ed25519_import_public(byte[] privKey);
|
||||
|
|
@ -201,6 +202,24 @@ public class Ed25519 extends NativeStruct {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive and store the Ed25519 public key from the loaded private key.
|
||||
* Must be called after importPrivateOnly() before signing.
|
||||
*
|
||||
* @throws WolfCryptException if native operation fails
|
||||
* @throws IllegalStateException if object fails to initialize or has been
|
||||
* released.
|
||||
*/
|
||||
public void makePublic()
|
||||
throws WolfCryptException, IllegalStateException {
|
||||
|
||||
checkStateAndInitialize();
|
||||
|
||||
synchronized (pointerLock) {
|
||||
wc_ed25519_make_public();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import private and public Ed25519 key.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -117,11 +117,12 @@ public class WolfCryptEdDSASignatureTest {
|
|||
}
|
||||
|
||||
/*
|
||||
* RFC 8032 Section 6 — Test Vector 1 (empty message)
|
||||
* draft-josefsson-eddsa-ed25519-02 §6 — Test Vector 1 (empty message)
|
||||
* (also cited as RFC 8032 §6; vectors from IETF draft)
|
||||
*/
|
||||
private static final byte[] TV1_SEED = hex(
|
||||
"9d61b19deffd5a60ba844af492ec2cc4" +
|
||||
"4449c5697b326919703bac031cae3d55");
|
||||
"4449c5697b326919703bac031cae7f60");
|
||||
private static final byte[] TV1_PUB = hex(
|
||||
"d75a980182b10ab7d54bfed3c964073a" +
|
||||
"0ee172f3daa62325af021a68f707511a");
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
|
|||
/**
|
||||
* JUnit4 tests for WolfCryptKeyAgreement X25519 (XDH).
|
||||
*
|
||||
* RFC 7748 Section 6.1 test vector used for correctness verification.
|
||||
* RFC 8037, Section A.6 test vector used for correctness verification.
|
||||
*/
|
||||
public class WolfCryptX25519KeyAgreementTest {
|
||||
|
||||
|
|
@ -95,27 +95,28 @@ public class WolfCryptX25519KeyAgreementTest {
|
|||
}
|
||||
|
||||
/*
|
||||
* RFC 7748 Section 6.1 — X25519 Diffie-Hellman test vector.
|
||||
* RFC 8037, Section A.6 — X25519 Diffie-Hellman test vector.
|
||||
* (Also cited in RFC 7748; canonical vectors per Wycheproof tcId 102.)
|
||||
* All values are 32-byte little-endian (raw X25519 wire format).
|
||||
*/
|
||||
private static final byte[] RFC_ALICE_PRIV = hex(
|
||||
"77076d0a7318a57d3c16c17251b26645" +
|
||||
"df91ef6f5eacc0aee9eefb22e65fc54e");
|
||||
"df4c2f87ebc0992ab177fba51db92c2a");
|
||||
private static final byte[] RFC_ALICE_PUB = hex(
|
||||
"8520f0098930a754748b7ddcb43ef75a" +
|
||||
"0dbf3a0d26381af4eba4a98eaa9b4e6a");
|
||||
private static final byte[] RFC_BOB_PRIV = hex(
|
||||
"5dab087e624a8a4b79e17f8b83800ee6" +
|
||||
"6f3bb1292618b6fd1c268f061c90d7fd");
|
||||
"6f3bb1292618b6fd1c2f8b27ff88e0eb");
|
||||
private static final byte[] RFC_BOB_PUB = hex(
|
||||
"de9edb7d7b7dc1b4d35b61c2ece43527" +
|
||||
"3cf1cfa7673a7ee35f19c7ddc4d7b1bf");
|
||||
"de9edb7d7b7dc1b4d35b61c2ece43537" +
|
||||
"3f8343c85b78674dadfc7e146f882b4f");
|
||||
private static final byte[] RFC_SHARED = hex(
|
||||
"4a5d9d5ba4ce2de1728e3bf480350f25" +
|
||||
"e07e21c947d19e3376f09b3c1e161742");
|
||||
|
||||
/**
|
||||
* RFC 7748 §6.1: Alice computes shared secret using her private key and
|
||||
* RFC 8037 §A.6: Alice computes shared secret using her private key and
|
||||
* Bob's public key; result must equal the known shared secret.
|
||||
*/
|
||||
@Test
|
||||
|
|
@ -133,12 +134,12 @@ public class WolfCryptX25519KeyAgreementTest {
|
|||
ka.doPhase(bobPub, true);
|
||||
byte[] shared = ka.generateSecret();
|
||||
|
||||
assertArrayEquals("RFC 7748 §6.1 Alice-side shared secret mismatch",
|
||||
assertArrayEquals("RFC 8037 §A.6 Alice-side shared secret mismatch",
|
||||
RFC_SHARED, shared);
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC 7748 §6.1: Bob computes shared secret using his private key and
|
||||
* RFC 8037 §A.6: Bob computes shared secret using his private key and
|
||||
* Alice's public key; result must equal the known shared secret.
|
||||
*/
|
||||
@Test
|
||||
|
|
@ -156,7 +157,7 @@ public class WolfCryptX25519KeyAgreementTest {
|
|||
ka.doPhase(alicePub, true);
|
||||
byte[] shared = ka.generateSecret();
|
||||
|
||||
assertArrayEquals("RFC 7748 §6.1 Bob-side shared secret mismatch",
|
||||
assertArrayEquals("RFC 8037 §A.6 Bob-side shared secret mismatch",
|
||||
RFC_SHARED, shared);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue