// Path: src/main/java/org/cryptomator/cryptolib/api/AuthenticationFailedException.java
// public class AuthenticationFailedException extends CryptoException {
// 
// 	public AuthenticationFailedException(String message) {
// 		super(message);
// 	}
// 
// 	public AuthenticationFailedException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// }
// 
// Path: src/main/java/org/cryptomator/cryptolib/api/Masterkey.java
// public class Masterkey extends DestroyableSecretKey {
// 
// 	private static final String KEY_ALGORITHM = "MASTERKEY";
// 	public static final String ENC_ALG = "AES";
// 	public static final String MAC_ALG = "HmacSHA256";
// 	public static final int SUBKEY_LEN_BYTES = 32;
// 
// 	public Masterkey(byte[] key) {
// 		super(checkKeyLength(key), KEY_ALGORITHM);
// 	}
// 
// 	private static byte[] checkKeyLength(byte[] key) {
// 		Preconditions.checkArgument(key.length == SUBKEY_LEN_BYTES + SUBKEY_LEN_BYTES, "Invalid raw key length %s", key.length);
// 		return key;
// 	}
// 
// 	public static Masterkey generate(SecureRandom csprng) {
// 		byte[] key = new byte[SUBKEY_LEN_BYTES + SUBKEY_LEN_BYTES];
// 		try {
// 			csprng.nextBytes(key);
// 			return new Masterkey(key);
// 		} finally {
// 			Arrays.fill(key, (byte) 0x00);
// 		}
// 	}
// 
// 	public static Masterkey from(DestroyableSecretKey encKey, DestroyableSecretKey macKey) {
// 		Preconditions.checkArgument(encKey.getEncoded().length == SUBKEY_LEN_BYTES, "Invalid key length of encKey");
// 		Preconditions.checkArgument(macKey.getEncoded().length == SUBKEY_LEN_BYTES, "Invalid key length of macKey");
// 		byte[] key = new byte[SUBKEY_LEN_BYTES + SUBKEY_LEN_BYTES];
// 		try {
// 			System.arraycopy(encKey.getEncoded(), 0, key, 0, SUBKEY_LEN_BYTES);
// 			System.arraycopy(macKey.getEncoded(), 0, key, SUBKEY_LEN_BYTES, SUBKEY_LEN_BYTES);
// 			return new Masterkey(key);
// 		} finally {
// 			Arrays.fill(key, (byte) 0x00);
// 		}
// 	}
// 
// 	@Override
// 	public Masterkey copy() {
// 		return new Masterkey(getEncoded());
// 	}
// 
// 	/**
// 	 * Get the encryption subkey.
// 	 *
// 	 * @return A new copy of the subkey used for encryption
// 	 */
// 	public DestroyableSecretKey getEncKey() {
// 		return new DestroyableSecretKey(getEncoded(), 0, SUBKEY_LEN_BYTES, ENC_ALG);
// 	}
// 
// 	/**
// 	 * Get the MAC subkey.
// 	 *
// 	 * @return A new copy of the subkey used for message authentication
// 	 */
// 	public DestroyableSecretKey getMacKey() {
// 		return new DestroyableSecretKey(getEncoded(), SUBKEY_LEN_BYTES, SUBKEY_LEN_BYTES, MAC_ALG);
// 	}
// 
// }


import com.google.common.io.BaseEncoding;
import org.cryptomator.cryptolib.api.AuthenticationFailedException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.siv.UnauthenticCiphertextException;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.UUID;
import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.UTF_8;
		return Stream.generate(UUID::randomUUID).map(UUID::toString).limit(100);
	}

	@DisplayName("encrypt and decrypt file names")
	@ParameterizedTest(name = "decrypt(encrypt({0}))")
	@MethodSource("filenameGenerator")
	public void testDeterministicEncryptionOfFilenames(String origName) throws AuthenticationFailedException {
		String encrypted1 = filenameCryptor.encryptFilename(BASE32, origName);
		String encrypted2 = filenameCryptor.encryptFilename(BASE32, origName);
		String decrypted = filenameCryptor.decryptFilename(BASE32, encrypted1);

		Assertions.assertEquals(encrypted1, encrypted2);
		Assertions.assertEquals(origName, decrypted);
	}

	@DisplayName("encrypt and decrypt file names with AD and custom encoding")
	@ParameterizedTest(name = "decrypt(encrypt({0}))")
	@MethodSource("filenameGenerator")
	public void testDeterministicEncryptionOfFilenamesWithCustomEncodingAndAssociatedData(String origName) throws AuthenticationFailedException {
		byte[] associdatedData = new byte[10];
		String encrypted1 = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), origName, associdatedData);
		String encrypted2 = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), origName, associdatedData);
		String decrypted = filenameCryptor.decryptFilename(BaseEncoding.base64Url(), encrypted1, associdatedData);

		Assertions.assertEquals(encrypted1, encrypted2);
		Assertions.assertEquals(origName, decrypted);
	}

	@Test
	@DisplayName("encrypt and decrypt 128 bit filename")
// Path: src/main/java/org/cryptomator/cryptolib/api/AuthenticationFailedException.java
// public class AuthenticationFailedException extends CryptoException {
// 
// 	public AuthenticationFailedException(String message) {
// 		super(message);
// 	}
// 
// 	public AuthenticationFailedException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// }
// 
// Path: src/main/java/org/cryptomator/cryptolib/api/Masterkey.java
// public class Masterkey extends DestroyableSecretKey {
// 
// 	private static final String KEY_ALGORITHM = "MASTERKEY";
// 	public static final String ENC_ALG = "AES";
// 	public static final String MAC_ALG = "HmacSHA256";
// 	public static final int SUBKEY_LEN_BYTES = 32;
// 
// 	public Masterkey(byte[] key) {
// 		super(checkKeyLength(key), KEY_ALGORITHM);
// 	}
// 
// 	private static byte[] checkKeyLength(byte[] key) {
// 		Preconditions.checkArgument(key.length == SUBKEY_LEN_BYTES + SUBKEY_LEN_BYTES, "Invalid raw key length %s", key.length);
// 		return key;
// 	}
// 
// 	public static Masterkey generate(SecureRandom csprng) {
// 		byte[] key = new byte[SUBKEY_LEN_BYTES + SUBKEY_LEN_BYTES];
// 		try {
// 			csprng.nextBytes(key);
// 			return new Masterkey(key);
// 		} finally {
// 			Arrays.fill(key, (byte) 0x00);
// 		}
// 	}
// 
// 	public static Masterkey from(DestroyableSecretKey encKey, DestroyableSecretKey macKey) {
// 		Preconditions.checkArgument(encKey.getEncoded().length == SUBKEY_LEN_BYTES, "Invalid key length of encKey");
// 		Preconditions.checkArgument(macKey.getEncoded().length == SUBKEY_LEN_BYTES, "Invalid key length of macKey");
// 		byte[] key = new byte[SUBKEY_LEN_BYTES + SUBKEY_LEN_BYTES];
// 		try {
// 			System.arraycopy(encKey.getEncoded(), 0, key, 0, SUBKEY_LEN_BYTES);
// 			System.arraycopy(macKey.getEncoded(), 0, key, SUBKEY_LEN_BYTES, SUBKEY_LEN_BYTES);
// 			return new Masterkey(key);
// 		} finally {
// 			Arrays.fill(key, (byte) 0x00);
// 		}
// 	}
// 
// 	@Override
// 	public Masterkey copy() {
// 		return new Masterkey(getEncoded());
// 	}
// 
// 	/**
// 	 * Get the encryption subkey.
// 	 *
// 	 * @return A new copy of the subkey used for encryption
// 	 */
// 	public DestroyableSecretKey getEncKey() {
// 		return new DestroyableSecretKey(getEncoded(), 0, SUBKEY_LEN_BYTES, ENC_ALG);
// 	}
// 
// 	/**
// 	 * Get the MAC subkey.
// 	 *
// 	 * @return A new copy of the subkey used for message authentication
// 	 */
// 	public DestroyableSecretKey getMacKey() {
// 		return new DestroyableSecretKey(getEncoded(), SUBKEY_LEN_BYTES, SUBKEY_LEN_BYTES, MAC_ALG);
// 	}
// 
// }


// Path: src/test/java/org/cryptomator/cryptolib/v2/FileNameCryptorImplTest.java
import com.google.common.io.BaseEncoding;
import org.cryptomator.cryptolib.api.AuthenticationFailedException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.siv.UnauthenticCiphertextException;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.UUID;
import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.UTF_8;

		return Stream.generate(UUID::randomUUID).map(UUID::toString).limit(100);
	}

	@DisplayName("encrypt and decrypt file names")
	@ParameterizedTest(name = "decrypt(encrypt({0}))")
	@MethodSource("filenameGenerator")
	public void testDeterministicEncryptionOfFilenames(String origName) throws AuthenticationFailedException {
		String encrypted1 = filenameCryptor.encryptFilename(BASE32, origName);
		String encrypted2 = filenameCryptor.encryptFilename(BASE32, origName);
		String decrypted = filenameCryptor.decryptFilename(BASE32, encrypted1);

		Assertions.assertEquals(encrypted1, encrypted2);
		Assertions.assertEquals(origName, decrypted);
	}

	@DisplayName("encrypt and decrypt file names with AD and custom encoding")
	@ParameterizedTest(name = "decrypt(encrypt({0}))")
	@MethodSource("filenameGenerator")
	public void testDeterministicEncryptionOfFilenamesWithCustomEncodingAndAssociatedData(String origName) throws AuthenticationFailedException {
		byte[] associdatedData = new byte[10];
		String encrypted1 = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), origName, associdatedData);
		String encrypted2 = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), origName, associdatedData);
		String decrypted = filenameCryptor.decryptFilename(BaseEncoding.base64Url(), encrypted1, associdatedData);

		Assertions.assertEquals(encrypted1, encrypted2);
		Assertions.assertEquals(origName, decrypted);
	}

	@Test
	@DisplayName("encrypt and decrypt 128 bit filename")
	public void testDeterministicEncryptionOf128bitFilename() throws AuthenticationFailedException {