![JAR search and dependency download from the Maven repository](/logo.png)
com.amazonaws.encryptionsdk.caching.CryptoMaterialsCache Maven / Gradle / Ivy
package com.amazonaws.encryptionsdk.caching;
import com.amazonaws.encryptionsdk.internal.Utils;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import java.util.Objects;
/**
* Represents a generic cache for cryptographic materials. MaterialsCaches store mappings from
* abstract bytestring identifiers to MaterialsResults and DecryptResults.
*
* In general, the materials cache is concerned about the proper storage of these materials, and
* managing size limits on the cache. While it stores statistics about cache usage limits, the
* enforcement of these limits is left to the caller (typically, a {@link
* CachingCryptoMaterialsManager}).
*
*
For encrypt, a cache implementation may store multiple cache entries for the same identifier.
* This allows for usage limits to be enforced even when doing multiple streaming requests in
* parallel. However, the cache is permitted to set a limit on the number of such entries (even as
* low as only allowing one entry per identifier), and if it does so should evict the excess
* entries.
*
*
Being a cache, a CryptoMaterialsCache is permitted to evict entries at any time. However, a
* caller can explicitly hint the cache to invalidate an entry in the encrypt-side cache. This is
* generally done when usage limits are exceeded. The cache is not required to respect this
* invalidation hint.
*
*
Likewise, the CacheHint passed to the put calls on caches will indicate the maximum lifetime
* of entries; the cache is allowed - but not required - to evict entries automatically upon
* expiration of this lifetime.
*/
public interface CryptoMaterialsCache {
/**
* Searches for an entry in the encrypt cache matching a particular cache identifier, and returns
* one if found.
*
* @param cacheId The identifier for the item in the cache
* @param usageIncrement The amount of usage to atomically add to the returned entry. This usage
* increment must be reflected in the getUsageStats() method on the returned cache entry.
* @return The entry, or null if not found or an error occurred
*/
EncryptCacheEntry getEntryForEncrypt(byte[] cacheId, final UsageStats usageIncrement);
/**
* Attempts to add a new entry to the encrypt cache to be returned on subsequent {@link
* #getEntryForEncrypt(byte[], UsageStats)} calls.
*
*
In the event that an error occurs when adding the entry to the cache, this function shall
* still return a EncryptCacheEntry instance, which shall act as if the cache entry was
* immediately evicted and/or invalidated.
*
* @param cacheId The identifier for the item in the cache
* @param encryptionMaterials The {@link EncryptionMaterials} to associate with this new entry
* @param initialUsage The initial usage stats for the cache entry
* @return A new, locked EncryptCacheEntry instance containing the given encryptionMaterials
*/
EncryptCacheEntry putEntryForEncrypt(
byte[] cacheId,
EncryptionMaterials encryptionMaterials,
CacheHint hint,
UsageStats initialUsage);
/**
* Searches for an entry in the encrypt cache matching a particular cache identifier, and returns
* one if found.
*
*
In the event of an error accessing the cache, this function will return null.
*
* @param cacheId The identifier for the item in the cache
* @return The cached decryption result, or null if none was found or an error occurred.
*/
DecryptCacheEntry getEntryForDecrypt(byte[] cacheId);
/**
* Adds a new entry to the decrypt cache. In the event of an error, this function will return
* silently without propagating the exception.
*
*
If an entry already exists for this cache ID, the cache may either overwrite the entry in
* its entirety, or update the creation timestamp for the existing entry, at its option.
*
* @param cacheId The identifier for the item in the cache
* @param decryptionMaterials The {@link DecryptionMaterials} to associate with the new entry.
*/
void putEntryForDecrypt(byte[] cacheId, DecryptionMaterials decryptionMaterials, CacheHint hint);
/**
* Contains some additional information associated with a cache entry. The cache receiving this
* hint may take some actions based on the hint, or it may ignore it entirely.
*/
interface CacheHint {
/**
* Returns the lifetime of the cache entry. This hint suggests to the cache that the entry will
* not be useful after the provided number of milliseconds passes, and suggests that the cache
* should discard the entry when this interval elapses even if it is not explicitly invalidated.
*
*
Note that this time counts from the creation of the entry, not from last use.
*
* @return The lifetime of this entry, in milliseconds. If the lifetime is unknown or
* irrelevant, this will return {@link Long#MAX_VALUE}.
*/
long getMaxAgeMillis();
}
/**
* Represents an entry in the encrypt cache, and provides methods for manipulating the entry.
*
*
Note that the EncryptCacheEntry Java object remains valid even after the cache entry is
* invalidated or evicted; getResult will still return a valid result, for example.
*/
interface EncryptCacheEntry {
/**
* @return The current usage statistics for this entry. The caller should be aware that these
* statistics may be stale by the time they are returned.
*/
UsageStats getUsageStats();
/** @return The unix timestamp at which this entry was added to the cache, in milliseconds */
long getEntryCreationTime();
/**
* @return The EncryptionMaterials associated with this cache entry. The encrypt completion
* callback should be a no-op.
*/
EncryptionMaterials getResult();
/** Suggests to the cache that this entry should be removed from the cache. */
default void invalidate() {}
}
final class UsageStats {
public static final UsageStats ZERO = new UsageStats(0, 0);
private final long bytesEncrypted;
private final long messagesEncrypted;
public UsageStats(long bytesEncrypted, long messagesEncrypted) {
if (bytesEncrypted < 0) {
throw new IllegalArgumentException("Negative bytes encrypted is not permitted");
}
if (messagesEncrypted < 0) {
throw new IllegalArgumentException("Negative messages encrypted is not permitted");
}
this.bytesEncrypted = bytesEncrypted;
this.messagesEncrypted = messagesEncrypted;
}
public long getBytesEncrypted() {
return bytesEncrypted;
}
public long getMessagesEncrypted() {
return messagesEncrypted;
}
/**
* Performs a pairwise add of two UsageStats objects. In the event of overflow, counters
* saturate at {@link Long#MAX_VALUE}
*
* @param other
* @return
*/
public UsageStats add(UsageStats other) {
return new UsageStats(
saturatingAdd(getBytesEncrypted(), other.getBytesEncrypted()),
saturatingAdd(getMessagesEncrypted(), other.getMessagesEncrypted()));
}
static long saturatingAdd(long a, long b) {
if (a < 0 || b < 0) {
throw new IllegalArgumentException("Negative usage values are not permitted");
}
return Utils.saturatingAdd(a, b);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UsageStats that = (UsageStats) o;
return getBytesEncrypted() == that.getBytesEncrypted()
&& getMessagesEncrypted() == that.getMessagesEncrypted();
}
@Override
public int hashCode() {
return Objects.hash(getBytesEncrypted(), getMessagesEncrypted());
}
@Override
public String toString() {
return "UsageStats{"
+ "bytesEncrypted="
+ bytesEncrypted
+ ", messagesEncrypted="
+ messagesEncrypted
+ '}';
}
}
/**
* Represents an entry in the decrypt cache, and provides methods for manipulating the entry.
*
*
Note that the DecryptCacheEntry JAva object remains valid even after the cache entry is
* invalidated or evicted; getResult will still return a valid result, for example.
*/
interface DecryptCacheEntry {
/** Returns the DecryptionMaterials associated with this entry. */
DecryptionMaterials getResult();
/** Advises the cache that this entry should be removed from the cache. */
void invalidate();
/** @return The unix timestamp at which this entry was added to the cache, in milliseconds */
long getEntryCreationTime();
}
}