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); - } }