diff --git a/pom.xml b/pom.xml
index 301a664..cbd7bf4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,11 +50,11 @@
1.7.1
-
+
- org.whispersystems
- curve25519-java
- 0.5.0
+ com.google.crypto.tink
+ tink
+ 1.9.0
diff --git a/src/main/java/dev/medzik/libcrypto/Curve25519.java b/src/main/java/dev/medzik/libcrypto/Curve25519.java
index 70a2b57..278bfa0 100644
--- a/src/main/java/dev/medzik/libcrypto/Curve25519.java
+++ b/src/main/java/dev/medzik/libcrypto/Curve25519.java
@@ -1,88 +1,47 @@
package dev.medzik.libcrypto;
+import com.google.crypto.tink.subtle.X25519;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
+import java.security.InvalidKeyException;
/**
- * Curve25519 implementation. This class is a wrapper around the WhisperSystems Curve25519 implementation
- * with a Hex encoding/decoding layer.
- * See Curve25519 on Wikipedia
+ * Curve25519 implementation using Google Tink.
*/
public class Curve25519 {
- private static final org.whispersystems.curve25519.Curve25519 curve25519 = org.whispersystems.curve25519.Curve25519.getInstance(org.whispersystems.curve25519.Curve25519.JAVA);
+ /**
+ * Generate a new X25519 key pair.
+ * @return X25519 key pair.
+ */
+ public static Curve25519KeyPair generateKeyPair() throws InvalidKeyException, DecoderException {
+ byte[] privateKey = X25519.generatePrivateKey();
+ return fromPrivateKey(Hex.encodeHexString(privateKey));
+ }
/**
- * Generate a new Curve25519 key pair.
- * @return Curve25519 key pair.
+ * Return a X25519 key pair from a private key.
+ * @param privateKey private key to recover (hex encoded)
+ * @return X25519 key pair.
*/
- public static Curve25519KeyPair generateKeyPair() {
- org.whispersystems.curve25519.Curve25519KeyPair keyPair = curve25519.generateKeyPair();
- return new Curve25519KeyPair(keyPair.getPublicKey(), keyPair.getPrivateKey());
+ public static Curve25519KeyPair fromPrivateKey(String privateKey) throws DecoderException, InvalidKeyException {
+ byte[] privateKeyBytes = Hex.decodeHex(privateKey);
+ byte[] publicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
+
+ return new Curve25519KeyPair(publicKeyBytes, publicKeyBytes);
}
/**
- * Calculate a shared secret given our private key and their public key.
+ * Compute a shared secret given our private key and their public key.
* @param ourPrivate our private key
* @param theirPublic their public key
* @return Shared secret.
*/
- public static String calculateAgreement(String ourPrivate, String theirPublic) throws DecoderException {
+ public static String computeSharedSecret(String ourPrivate, String theirPublic) throws DecoderException, InvalidKeyException {
byte[] outPrivateBytes = Hex.decodeHex(ourPrivate);
byte[] theirPublicBytes = Hex.decodeHex(theirPublic);
- byte[] sharedSecret = curve25519.calculateAgreement(outPrivateBytes, theirPublicBytes);
+ byte[] sharedSecret = X25519.computeSharedSecret(outPrivateBytes, theirPublicBytes);
return Hex.encodeHexString(sharedSecret);
}
-
- /**
- * Calculate a Curve25519 signature given a private key and a message.
- * @param privateKey private key to signing
- * @param message message to sign (hex encoded)
- * @return Curve25519 signature.
- */
- public static String calculateSignature(String privateKey, String message) throws DecoderException {
- byte[] privateKeyBytes = Hex.decodeHex(privateKey);
- byte[] messageBytes = Hex.decodeHex(message);
-
- byte[] signature = curve25519.calculateSignature(privateKeyBytes, messageBytes);
-
- return Hex.encodeHexString(signature);
- }
-
- /**
- * Calculate a Curve25519 signature given a private key and a message.
- * @param privateKey private key to signing
- * @param message message to sign
- * @return Curve25519 signature.
- */
- public static String calculateSignature(String privateKey, byte[] message) throws DecoderException {
- return calculateSignature(privateKey, Hex.encodeHexString(message));
- }
-
- /**
- * Verify a Curve25519 signature given a public key, message, and signature.
- * @param publicKey public key to verify
- * @param message message to verify (hex encoded)
- * @param signature signature to verify
- * @return True if the signature is valid, false otherwise.
- */
- public static boolean verifySignature(String publicKey, String message, String signature) throws DecoderException {
- byte[] publicKeyBytes = Hex.decodeHex(publicKey);
- byte[] messageBytes = Hex.decodeHex(message);
- byte[] signatureBytes = Hex.decodeHex(signature);
-
- return curve25519.verifySignature(publicKeyBytes, messageBytes, signatureBytes);
- }
-
- /**
- * Verify a Curve25519 signature given a public key, message, and signature.
- * @param publicKey public key to verify
- * @param message message to verify
- * @param signature signature to verify
- * @return True if the signature is valid, false otherwise.
- */
- public static boolean verifySignature(String publicKey, byte[] message, String signature) throws DecoderException {
- return verifySignature(publicKey, Hex.encodeHexString(message), signature);
- }
}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 0636dab..416807e 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -5,8 +5,8 @@
// Password4j (used for argon2 implementation)
requires password4j;
- // Curve25519 implementation
- requires curve25519.java;
+ // Google Tink (used for Curve25519 implementation)
+ requires com.google.crypto.tink;
exports dev.medzik.libcrypto;
}
diff --git a/src/test/java/dev/medzik/libcrypto/Curve25519Tests.java b/src/test/java/dev/medzik/libcrypto/Curve25519Tests.java
index bade9c3..f047789 100644
--- a/src/test/java/dev/medzik/libcrypto/Curve25519Tests.java
+++ b/src/test/java/dev/medzik/libcrypto/Curve25519Tests.java
@@ -3,9 +3,12 @@
import org.apache.commons.codec.DecoderException;
import org.junit.jupiter.api.Test;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+
public class Curve25519Tests {
@Test
- public void testGenerateKeyPair() {
+ public void testGenerateKeyPair() throws DecoderException, InvalidKeyException {
Curve25519KeyPair keyPair = Curve25519.generateKeyPair();
assert keyPair.getPrivateKey().length() == 64;
@@ -13,83 +16,46 @@ public void testGenerateKeyPair() {
}
@Test
- public void testCalculateAgreement() throws DecoderException {
+ public void textComputeSharedSecret() throws DecoderException, InvalidKeyException {
Curve25519KeyPair our = Curve25519.generateKeyPair();
Curve25519KeyPair their = Curve25519.generateKeyPair();
String ourPrivate = our.getPrivateKey();
String theirPublic = their.getPublicKey();
- String sharedSecret = Curve25519.calculateAgreement(ourPrivate, theirPublic);
+ String sharedSecret = Curve25519.computeSharedSecret(ourPrivate, theirPublic);
assert sharedSecret.length() == 64;
}
@Test
- public void testCalculateAgreementEncrypt() throws DecoderException, EncryptException {
+ public void textComputeSharedSecret2() throws DecoderException, InvalidKeyException {
+ String privateKey = "3845bead1f44408ee436c742291f1362489eeaaa9daebd480b1c3e4bc528cb48";
+ String publicKey = "9d49b72cf49defc6748c67ab274a1c2f096362ef3b2d691793686589760b4e25";
+
+ String sharedSecret = Curve25519.computeSharedSecret(privateKey, publicKey);
+
+ assert sharedSecret.equals("2bebf3c397ab3c79db9aeeb2c1523ab4a32bd1ae335a19cd47e35983a5184d09");
+ }
+
+ @Test
+ public void testCalculateAgreementEncrypt() throws DecoderException, EncryptException, InvalidKeyException {
Curve25519KeyPair keyPair = Curve25519.generateKeyPair();
String ourPrivate = keyPair.getPrivateKey();
String theirPublic = keyPair.getPublicKey();
- String sharedSecret = Curve25519.calculateAgreement(ourPrivate, theirPublic);
+ String sharedSecret = Curve25519.computeSharedSecret(ourPrivate, theirPublic);
String cipherText = AES.encrypt(AES.GCM, sharedSecret, "Hello, world!");
String theirPrivate = keyPair.getPrivateKey();
String outPublic = keyPair.getPublicKey();
- String sharedSecretTwo = Curve25519.calculateAgreement(theirPrivate, outPublic);
+ String sharedSecretTwo = Curve25519.computeSharedSecret(theirPrivate, outPublic);
String plainText = AES.decrypt(AES.GCM, sharedSecretTwo, cipherText);
assert plainText.equals("Hello, world!");
}
-
- @Test
- public void testCalculateSignature() throws DecoderException {
- Curve25519KeyPair keyPair = Curve25519.generateKeyPair();
-
- String privateKey = keyPair.getPrivateKey();
- byte[] message = "Hello, world!".getBytes();
-
- String signature = Curve25519.calculateSignature(privateKey, message);
-
- assert signature.length() == 128;
- }
-
- @Test
- public void testVerifySignature() throws DecoderException {
- Curve25519KeyPair keyPair = Curve25519.generateKeyPair();
-
- String publicKey = keyPair.getPublicKey();
- byte[] message = "Hello, world!".getBytes();
- String signature = Curve25519.calculateSignature(keyPair.getPrivateKey(), message);
-
- assert Curve25519.verifySignature(publicKey, message, signature);
- }
-
- @Test
- public void testVerifySignatureInvalidSignature() throws DecoderException {
- Curve25519KeyPair keyPair = Curve25519.generateKeyPair();
-
- String publicKey = keyPair.getPublicKey();
- byte[] message = "Hello, world!".getBytes();
- String signature = Curve25519.calculateSignature(keyPair.getPrivateKey(), message);
-
- signature = signature.substring(1, signature.length() - 1);
-
- assert !Curve25519.verifySignature(publicKey, message, signature);
- }
-
- @Test
- public void testVerifySignatureInvalidMessage() throws DecoderException {
- Curve25519KeyPair keyPair = Curve25519.generateKeyPair();
-
- String publicKey = keyPair.getPublicKey();
- byte[] message = "Hello, world!".getBytes();
- String signature = Curve25519.calculateSignature(keyPair.getPrivateKey(), message);
-
- assert !Curve25519.verifySignature(publicKey, "Goodbye, world!".getBytes(), signature);
- }
}