diff --git a/java/src/main/java/dev/medzik/libcrypto/Aes.java b/java/src/main/java/dev/medzik/libcrypto/Aes.java index de81965..308d94b 100644 --- a/java/src/main/java/dev/medzik/libcrypto/Aes.java +++ b/java/src/main/java/dev/medzik/libcrypto/Aes.java @@ -1,15 +1,9 @@ package dev.medzik.libcrypto; -import javax.crypto.BadPaddingException; import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; public final class Aes { @@ -20,12 +14,14 @@ public final class Aes { /** * Encrypts the given clear bytes using AES with the given key and random IV. + * * @param type AES type to use * @param key secret key to use for encryption * @param clearBytes clear bytes to encrypt (UTF-8) * @return Encrypted cipher (hex encoded) + * @throws AesException if encryption fails */ - public static String encrypt(AesType type, byte[] key, byte[] clearBytes) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + public static String encrypt(AesType type, byte[] key, byte[] clearBytes) throws AesException { // generate random IV byte[] iv = Random.randBytes(type.getIvLength()); @@ -37,15 +33,17 @@ public static String encrypt(AesType type, byte[] key, byte[] clearBytes) throws try { cipher = Cipher.getInstance(type.getMode()); cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); - } catch (InvalidAlgorithmParameterException | - NoSuchPaddingException | - NoSuchAlgorithmException e) { - // never happen - throw new RuntimeException(e); + } catch (Exception e) { + throw new AesException("Failed to init cipher", e); } // encrypt - byte[] cipherBytes = cipher.doFinal(clearBytes); + byte[] cipherBytes; + try { + cipherBytes = cipher.doFinal(clearBytes); + } catch (Exception e) { + throw new AesException("Failed to finalize encryption", e); + } // return IV + cipher text as hex string return Hex.encode(iv) + Hex.encode(cipherBytes); @@ -53,12 +51,14 @@ public static String encrypt(AesType type, byte[] key, byte[] clearBytes) throws /** * Decrypts the given cipher text using AES with the given key. + * * @param type AES type to use * @param key secret key to use for decryption * @param cipherText cipher text to decrypt (hex encoded) * @return Decrypted bytes + * @throws AesException if decryption fails */ - public static byte[] decrypt(AesType type, byte[] key, String cipherText) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + public static byte[] decrypt(AesType type, byte[] key, String cipherText) throws AesException { // get IV length in hex string int ivLength = type.getIvLength() * 2; @@ -74,15 +74,16 @@ public static byte[] decrypt(AesType type, byte[] key, String cipherText) throws try { cipher = Cipher.getInstance(type.getMode()); cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); - } catch (InvalidAlgorithmParameterException | - NoSuchPaddingException | - NoSuchAlgorithmException e) { - // never happen - throw new RuntimeException(e); + } catch (Exception e) { + throw new AesException("Failed to init cipher", e); } // decrypt - return cipher.doFinal(cipherBytes); + try { + return cipher.doFinal(cipherBytes); + } catch (Exception e) { + throw new AesException("Failed to finalize decryption", e); + } } private static AlgorithmParameterSpec getParameterSpec(AesType type, byte[] iv) { diff --git a/java/src/main/java/dev/medzik/libcrypto/AesException.java b/java/src/main/java/dev/medzik/libcrypto/AesException.java new file mode 100644 index 0000000..78c04bd --- /dev/null +++ b/java/src/main/java/dev/medzik/libcrypto/AesException.java @@ -0,0 +1,7 @@ +package dev.medzik.libcrypto; + +public class AesException extends Exception { + public AesException(String message, Exception cause) { + super(message, cause); + } +} diff --git a/java/src/main/java/dev/medzik/libcrypto/Argon2EncodingUtils.java b/java/src/main/java/dev/medzik/libcrypto/Argon2EncodingUtils.java index ee69abb..6ba7e4c 100644 --- a/java/src/main/java/dev/medzik/libcrypto/Argon2EncodingUtils.java +++ b/java/src/main/java/dev/medzik/libcrypto/Argon2EncodingUtils.java @@ -11,6 +11,7 @@ public final class Argon2EncodingUtils { /** * Encodes the given hash and parameters to a string. + * * @param hash hash to encode * @return encoded hash in argon2 format */ @@ -42,6 +43,7 @@ public static String encode(Argon2Hash hash) { /** * Decodes the given Argon2 encoded hash. + * * @param encodedHash encoded hash in argon2 format * @return {@link Argon2Hash} */ diff --git a/java/src/main/java/dev/medzik/libcrypto/Argon2Hash.java b/java/src/main/java/dev/medzik/libcrypto/Argon2Hash.java index c63d9ae..40fb656 100644 --- a/java/src/main/java/dev/medzik/libcrypto/Argon2Hash.java +++ b/java/src/main/java/dev/medzik/libcrypto/Argon2Hash.java @@ -1,8 +1,6 @@ package dev.medzik.libcrypto; -/** - * Object representation of argon2 hash. - */ +/** Object representation of argon2 hash. */ public final class Argon2Hash { private final Argon2Type type; private final int version; diff --git a/java/src/main/java/dev/medzik/libcrypto/Argon2Type.java b/java/src/main/java/dev/medzik/libcrypto/Argon2Type.java index 951245a..541b900 100644 --- a/java/src/main/java/dev/medzik/libcrypto/Argon2Type.java +++ b/java/src/main/java/dev/medzik/libcrypto/Argon2Type.java @@ -15,8 +15,8 @@ public Argon2 toPassword4jType() { return Argon2.I; case ID: return Argon2.ID; - default: - throw new IllegalStateException("Unexpected value: " + this); } + // never happens + return null; } } diff --git a/java/src/main/java/dev/medzik/libcrypto/Hex.java b/java/src/main/java/dev/medzik/libcrypto/Hex.java index 3eb9a6c..3f04ca5 100644 --- a/java/src/main/java/dev/medzik/libcrypto/Hex.java +++ b/java/src/main/java/dev/medzik/libcrypto/Hex.java @@ -47,7 +47,10 @@ public static String encode(byte[] bytes) { return result.toString(); } - /** Decodes a hex string to a byte array. */ + /** + * Decodes a hex string to a byte array. + * @throws IllegalArgumentException if the input is not hexadecimal + */ public static byte[] decode(String hex) { if (hex.length() % 2 != 0) { throw new IllegalArgumentException("Expected a string of even length"); diff --git a/java/src/main/java/dev/medzik/libcrypto/Random.java b/java/src/main/java/dev/medzik/libcrypto/Random.java index ca901d9..86a5d64 100644 --- a/java/src/main/java/dev/medzik/libcrypto/Random.java +++ b/java/src/main/java/dev/medzik/libcrypto/Random.java @@ -3,13 +3,10 @@ import java.security.SecureRandom; public final class Random { - /** - * Generate a random byte array. - * @param size length of salt slice in bytes - */ - public static byte[] randBytes(int size) { + /** Generate a random byte array. */ + public static byte[] randBytes(int length) { SecureRandom rd = new SecureRandom(); - byte[] salt = new byte[size]; + byte[] salt = new byte[length]; rd.nextBytes(salt); return salt; } diff --git a/java/src/main/java/dev/medzik/libcrypto/X25519.java b/java/src/main/java/dev/medzik/libcrypto/X25519.java index 3e95b77..4563f87 100644 --- a/java/src/main/java/dev/medzik/libcrypto/X25519.java +++ b/java/src/main/java/dev/medzik/libcrypto/X25519.java @@ -62,8 +62,7 @@ public static byte[] generatePrivateKey() { * @return 32-byte shared secret * @throws InvalidKeyException when {@code ourPrivate} is not 32-byte or {@code theirPublic} is invalid. */ - public static byte[] computeSharedSecret(byte[] ourPrivate, byte[] theirPublic) - throws InvalidKeyException { + public static byte[] computeSharedSecret(byte[] ourPrivate, byte[] theirPublic) throws InvalidKeyException { if (ourPrivate.length != Field25519.FIELD_LEN) { throw new InvalidKeyException("Private key must have 32 bytes."); } diff --git a/java/src/test/java/dev/medzik/libcrypto/AesTests.java b/java/src/test/java/dev/medzik/libcrypto/AesTests.java index 4fe2413..3f78216 100644 --- a/java/src/test/java/dev/medzik/libcrypto/AesTests.java +++ b/java/src/test/java/dev/medzik/libcrypto/AesTests.java @@ -2,15 +2,11 @@ import org.junit.jupiter.api.Test; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import java.security.InvalidKeyException; - import static org.junit.jupiter.api.Assertions.assertEquals; public final class AesTests { @Test - public void testCBCEncryptAndDecrypt() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + public void testCBCEncryptAndDecrypt() throws AesException { byte[] secretKey = Hex.decode("82fd4cefd6efde36171900b469bae4e06863cb70f80b4e216e44eeb0cf30460b"); String input = "Hello World!"; @@ -21,7 +17,7 @@ public void testCBCEncryptAndDecrypt() throws IllegalBlockSizeException, BadPadd } @Test - public void testCBCDecrypt() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + public void testCBCDecrypt() throws AesException { byte[] secretKey = Hex.decode("82fd4cefd6efde36171900b469bae4e06863cb70f80b4e216e44eeb0cf30460b"); String cipherText = "ae77d812f4494a766a94b5dff8e7aa3c8408544b9fd30cd13b886cc5dd1b190e"; @@ -31,7 +27,7 @@ public void testCBCDecrypt() throws IllegalBlockSizeException, BadPaddingExcepti } @Test - public void testGCMEncryptAndDecrypt() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + public void testGCMEncryptAndDecrypt() throws AesException { byte[] secretKey = Hex.decode("82fd4cefd6efde36171900b469bae4e06863cb70f80b4e216e44eeb0cf30460b"); String input = "Hello World!"; @@ -42,7 +38,7 @@ public void testGCMEncryptAndDecrypt() throws IllegalBlockSizeException, BadPadd } @Test - void testGCMDecrypt() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + void testGCMDecrypt() throws AesException { byte[] secretKey = Hex.decode("82fd4cefd6efde36171900b469bae4e06863cb70f80b4e216e44eeb0cf30460b"); String cipherText = "0996c65a72a60e748415dc6d32da1d4dcb65f41c71d4bec9554424218839b5d4b9d9195e5eea9d"; diff --git a/java/src/test/java/dev/medzik/libcrypto/ReadmeTests.java b/java/src/test/java/dev/medzik/libcrypto/ReadmeTests.java index 77ba4a2..6cd8a6c 100644 --- a/java/src/test/java/dev/medzik/libcrypto/ReadmeTests.java +++ b/java/src/test/java/dev/medzik/libcrypto/ReadmeTests.java @@ -2,8 +2,6 @@ import org.junit.jupiter.api.Test; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; import java.security.InvalidKeyException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -11,7 +9,7 @@ public final class ReadmeTests { @Test - public void testAesGcmEncryptDecryption() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + public void testAesGcmEncryptDecryption() throws AesException { String clearText = "hello world"; // Key used for encryption (for example, argon2 hash in hex string) String secretKey = "82fd4cefd6efde36171900b469bae4e06863cb70f80b4e216e44eeb0cf30460b"; @@ -47,7 +45,7 @@ public void testArgon2Hash() { } @Test - public void testX25519ExchangeKeys() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + public void testX25519ExchangeKeys() throws InvalidKeyException, AesException { // Generate private key for Bob byte[] bobPrivateKey = X25519.generatePrivateKey(); byte[] bobPublicKey = X25519.publicFromPrivate(bobPrivateKey);