// Path: src/main/java/org/cryptomator/cryptolib/common/MessageDigestSupplier.java
// public final class MessageDigestSupplier {
// 
// 	public static final MessageDigestSupplier SHA1 = new MessageDigestSupplier("SHA-1");
// 	public static final MessageDigestSupplier SHA256 = new MessageDigestSupplier("SHA-256");
// 
// 	private final String digestAlgorithm;
// 	private final ObjectPool<MessageDigest> mdPool;
// 
// 	public MessageDigestSupplier(String digestAlgorithm) {
// 		this.digestAlgorithm = digestAlgorithm;
// 		this.mdPool = new ObjectPool<>(this::createMessageDigest);
// 		try (ObjectPool.Lease<MessageDigest> lease = mdPool.get()) {
// 			lease.get(); // eagerly initialize to provoke exceptions
// 		}
// 	}
// 
// 	private MessageDigest createMessageDigest() {
// 		try {
// 			return MessageDigest.getInstance(digestAlgorithm);
// 		} catch (NoSuchAlgorithmException e) {
// 			throw new IllegalArgumentException("Invalid digest algorithm.", e);
// 		}
// 	}
// 
// 	/**
// 	 * Leases a reusable MessageDigest.
// 	 *
// 	 * @return A ReusableMessageDigest instance holding a refurbished MessageDigest
// 	 */
// 	public ObjectPool.Lease<MessageDigest> instance() {
// 		ObjectPool.Lease<MessageDigest> lease = mdPool.get();
// 		lease.get().reset();
// 		return lease;
// 	}
// 
// 	/**
// 	 * Creates a new MessageDigest.
// 	 *
// 	 * @return New MessageDigest instance
// 	 * @deprecated Use {@link #instance()}
// 	 */
// 	@Deprecated
// 	public MessageDigest get() {
// 		final MessageDigest result = createMessageDigest();
// 		result.reset();
// 		return result;
// 	}
// 
// }
// 
// Path: src/main/java/org/cryptomator/cryptolib/common/ObjectPool.java
// public class ObjectPool<T> {
// 
// 	private final Queue<WeakReference<T>> returnedInstances;
// 	private final Supplier<T> factory;
// 
// 	public ObjectPool(Supplier<T> factory) {
// 		this.returnedInstances = new ConcurrentLinkedQueue<>();
// 		this.factory = factory;
// 	}
// 
// 	public Lease<T> get() {
// 		WeakReference<T> ref;
// 		while ((ref = returnedInstances.poll()) != null) {
// 			T cached = ref.get();
// 			if (cached != null) {
// 				return new Lease<>(this, cached);
// 			}
// 		}
// 		return new Lease<>(this, factory.get());
// 	}
// 
// 	/**
// 	 * A holder for resource leased from an {@link ObjectPool}.
// 	 * This is basically an {@link AutoCloseable autocloseable} {@link Supplier} that is intended to be used
// 	 * via try-with-resource blocks.
// 	 *
// 	 * @param <T> Type of the leased instance
// 	 */
// 	public static class Lease<T> implements AutoCloseable, Supplier<T> {
// 
// 		private final ObjectPool<T> pool;
// 		private T obj;
// 
// 		private Lease(ObjectPool<T> pool, T obj) {
// 			this.pool = pool;
// 			this.obj = obj;
// 		}
// 
// 		public T get() {
// 			return obj;
// 		}
// 
// 		@Override
// 		public void close() {
// 			pool.returnedInstances.add(new WeakReference<>(obj));
// 			obj = null;
// 		}
// 	}
// 
// }


import org.cryptomator.cryptolib.common.MessageDigestSupplier;
import org.cryptomator.cryptolib.common.ObjectPool;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;
package org.cryptomator.cryptolib.ecies;



@FunctionalInterface
public interface KeyDerivationFunction {

	KeyDerivationFunction ANSI_X963_SHA256_KDF = (sharedSecret, keyDataLen) -> ansiX963sha256Kdf(sharedSecret, new byte[0], keyDataLen);

	/**
	 * Derives a key of desired length
	 *
	 * @param sharedSecret A shared secret
	 * @param keyDataLen   Desired key length (in bytes)
	 * @return key data
	 */
	byte[] deriveKey(byte[] sharedSecret, int keyDataLen);

	/**
	 * Performs <a href="https://www.secg.org/sec1-v2.pdf">ANSI-X9.63-KDF</a> with SHA-256
	 *
	 * @param sharedSecret A shared secret
	 * @param sharedInfo   Additional authenticated data
	 * @param keyDataLen   Desired key length (in bytes)
	 * @return key data
	 */
	static byte[] ansiX963sha256Kdf(byte[] sharedSecret, byte[] sharedInfo, int keyDataLen) {
		// max input length is 2^64 - 1, see https://doi.org/10.6028/NIST.SP.800-56Cr2, Table 1
		int hashLen = 32; // fixed digest length for SHA-256 in bytes

		// These two checks must be performed according to spec. However with 32 bit integers, we can't exceed any limits anyway:
		assert BigInteger.valueOf(4L + sharedSecret.length + sharedInfo.length).compareTo(BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE)) < 0 : "input larger than hashmaxlen";
		assert keyDataLen < (1L << 32 - 1) * hashLen : "keyDataLen larger than hashLen × (2^32 − 1)";

		ByteBuffer counter = ByteBuffer.allocate(Integer.BYTES);
		assert ByteOrder.BIG_ENDIAN.equals(counter.order());
		int n = (keyDataLen + hashLen - 1) / hashLen;
		byte[] buffer = new byte[n * hashLen];
// Path: src/main/java/org/cryptomator/cryptolib/common/MessageDigestSupplier.java
// public final class MessageDigestSupplier {
// 
// 	public static final MessageDigestSupplier SHA1 = new MessageDigestSupplier("SHA-1");
// 	public static final MessageDigestSupplier SHA256 = new MessageDigestSupplier("SHA-256");
// 
// 	private final String digestAlgorithm;
// 	private final ObjectPool<MessageDigest> mdPool;
// 
// 	public MessageDigestSupplier(String digestAlgorithm) {
// 		this.digestAlgorithm = digestAlgorithm;
// 		this.mdPool = new ObjectPool<>(this::createMessageDigest);
// 		try (ObjectPool.Lease<MessageDigest> lease = mdPool.get()) {
// 			lease.get(); // eagerly initialize to provoke exceptions
// 		}
// 	}
// 
// 	private MessageDigest createMessageDigest() {
// 		try {
// 			return MessageDigest.getInstance(digestAlgorithm);
// 		} catch (NoSuchAlgorithmException e) {
// 			throw new IllegalArgumentException("Invalid digest algorithm.", e);
// 		}
// 	}
// 
// 	/**
// 	 * Leases a reusable MessageDigest.
// 	 *
// 	 * @return A ReusableMessageDigest instance holding a refurbished MessageDigest
// 	 */
// 	public ObjectPool.Lease<MessageDigest> instance() {
// 		ObjectPool.Lease<MessageDigest> lease = mdPool.get();
// 		lease.get().reset();
// 		return lease;
// 	}
// 
// 	/**
// 	 * Creates a new MessageDigest.
// 	 *
// 	 * @return New MessageDigest instance
// 	 * @deprecated Use {@link #instance()}
// 	 */
// 	@Deprecated
// 	public MessageDigest get() {
// 		final MessageDigest result = createMessageDigest();
// 		result.reset();
// 		return result;
// 	}
// 
// }
// 
// Path: src/main/java/org/cryptomator/cryptolib/common/ObjectPool.java
// public class ObjectPool<T> {
// 
// 	private final Queue<WeakReference<T>> returnedInstances;
// 	private final Supplier<T> factory;
// 
// 	public ObjectPool(Supplier<T> factory) {
// 		this.returnedInstances = new ConcurrentLinkedQueue<>();
// 		this.factory = factory;
// 	}
// 
// 	public Lease<T> get() {
// 		WeakReference<T> ref;
// 		while ((ref = returnedInstances.poll()) != null) {
// 			T cached = ref.get();
// 			if (cached != null) {
// 				return new Lease<>(this, cached);
// 			}
// 		}
// 		return new Lease<>(this, factory.get());
// 	}
// 
// 	/**
// 	 * A holder for resource leased from an {@link ObjectPool}.
// 	 * This is basically an {@link AutoCloseable autocloseable} {@link Supplier} that is intended to be used
// 	 * via try-with-resource blocks.
// 	 *
// 	 * @param <T> Type of the leased instance
// 	 */
// 	public static class Lease<T> implements AutoCloseable, Supplier<T> {
// 
// 		private final ObjectPool<T> pool;
// 		private T obj;
// 
// 		private Lease(ObjectPool<T> pool, T obj) {
// 			this.pool = pool;
// 			this.obj = obj;
// 		}
// 
// 		public T get() {
// 			return obj;
// 		}
// 
// 		@Override
// 		public void close() {
// 			pool.returnedInstances.add(new WeakReference<>(obj));
// 			obj = null;
// 		}
// 	}
// 
// }


// Path: src/main/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunction.java
import org.cryptomator.cryptolib.common.MessageDigestSupplier;
import org.cryptomator.cryptolib.common.ObjectPool;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;

package org.cryptomator.cryptolib.ecies;



@FunctionalInterface
public interface KeyDerivationFunction {

	KeyDerivationFunction ANSI_X963_SHA256_KDF = (sharedSecret, keyDataLen) -> ansiX963sha256Kdf(sharedSecret, new byte[0], keyDataLen);

	/**
	 * Derives a key of desired length
	 *
	 * @param sharedSecret A shared secret
	 * @param keyDataLen   Desired key length (in bytes)
	 * @return key data
	 */
	byte[] deriveKey(byte[] sharedSecret, int keyDataLen);

	/**
	 * Performs <a href="https://www.secg.org/sec1-v2.pdf">ANSI-X9.63-KDF</a> with SHA-256
	 *
	 * @param sharedSecret A shared secret
	 * @param sharedInfo   Additional authenticated data
	 * @param keyDataLen   Desired key length (in bytes)
	 * @return key data
	 */
	static byte[] ansiX963sha256Kdf(byte[] sharedSecret, byte[] sharedInfo, int keyDataLen) {
		// max input length is 2^64 - 1, see https://doi.org/10.6028/NIST.SP.800-56Cr2, Table 1
		int hashLen = 32; // fixed digest length for SHA-256 in bytes

		// These two checks must be performed according to spec. However with 32 bit integers, we can't exceed any limits anyway:
		assert BigInteger.valueOf(4L + sharedSecret.length + sharedInfo.length).compareTo(BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE)) < 0 : "input larger than hashmaxlen";
		assert keyDataLen < (1L << 32 - 1) * hashLen : "keyDataLen larger than hashLen × (2^32 − 1)";

		ByteBuffer counter = ByteBuffer.allocate(Integer.BYTES);
		assert ByteOrder.BIG_ENDIAN.equals(counter.order());
		int n = (keyDataLen + hashLen - 1) / hashLen;
		byte[] buffer = new byte[n * hashLen];
		try (ObjectPool.Lease<MessageDigest> sha256 = MessageDigestSupplier.SHA256.instance()) {