chore(text): source code format

This commit is contained in:
2026-07-30 20:28:43 +02:00
parent bc68e0433c
commit dedd16f584
262 changed files with 4867 additions and 5717 deletions

View File

@@ -16,29 +16,31 @@ import zeroecho.core.spec.ContextSpec;
/**
* Immutable value descriptor of one algorithm context capability.
*
* <p>The default specification is resolved once during provider construction.
* All components therefore have stable value semantics and are safe for
* concurrent reads.</p>
* <p>
* The default specification is resolved once during provider construction. All
* components therefore have stable value semantics and are safe for concurrent
* reads.
* </p>
*
* @param algorithmId canonical algorithm identifier
* @param family algorithm family
* @param role supported key usage
* @param family algorithm family
* @param role supported key usage
* @param contextType produced context type
* @param keyType accepted key type
* @param specType accepted specification type
* @param keyType accepted key type
* @param specType accepted specification type
* @param defaultSpec non-null resolved default specification
* @since 1.0
*/
public record Capability(String algorithmId, AlgorithmFamily family, KeyUsage role,
Class<? extends CryptoContext> contextType, Class<? extends Key> keyType,
Class<? extends ContextSpec> specType, ContextSpec defaultSpec) {
Class<? extends CryptoContext> contextType, Class<? extends Key> keyType, Class<? extends ContextSpec> specType,
ContextSpec defaultSpec) {
/**
* Validates the capability metadata.
*
* @throws NullPointerException if a component is {@code null}
* @throws IllegalArgumentException if {@code defaultSpec} is incompatible
* with {@code specType}
* @throws NullPointerException if a component is {@code null}
* @throws IllegalArgumentException if {@code defaultSpec} is incompatible with
* {@code specType}
*/
public Capability {
Objects.requireNonNull(algorithmId, "algorithmId must not be null");

View File

@@ -106,8 +106,8 @@ import zeroecho.core.spi.SymmetricKeyImporter;
* <p>
* <b>Security note:</b> Algorithms must enforce strong validation of keys and
* specs during registration and
* {@link #createContext(KeyUsage, Key, ContextSpec)} to
* prevent downgrade or misuse attacks.
* {@link #createContext(KeyUsage, Key, ContextSpec)} to prevent downgrade or
* misuse attacks.
* </p>
*
* @since 1.0
@@ -123,16 +123,11 @@ public abstract class CryptoAlgorithm { // NOPMD
private final List<Capability> capabilities = new ArrayList<>();
private final Map<KeyUsage, List<RoleBinding<?, ?, ?>>> ctxBindings = new EnumMap<>(KeyUsage.class);
private final Map<Class<? extends AlgorithmKeySpec>, AsymmetricKeyPairGenerator<?>> keyPairGenerators =
new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, PublicKeyImporter<?>> publicKeyImporters =
new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, PrivateKeyImporter<?>> privateKeyImporters =
new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, SymmetricKeyGenerator<?>> symmetricKeyGenerators =
new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, SymmetricKeyImporter<?>> symmetricKeyImporters =
new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, AsymmetricKeyPairGenerator<?>> keyPairGenerators = new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, PublicKeyImporter<?>> publicKeyImporters = new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, PrivateKeyImporter<?>> privateKeyImporters = new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, SymmetricKeyGenerator<?>> symmetricKeyGenerators = new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, SymmetricKeyImporter<?>> symmetricKeyImporters = new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, AlgorithmKeySpec> asymmetricDefaults = new LinkedHashMap<>();
private final Map<Class<? extends AlgorithmKeySpec>, AlgorithmKeySpec> symmetricDefaults = new LinkedHashMap<>();
@@ -296,8 +291,9 @@ public abstract class CryptoAlgorithm { // NOPMD
* <p>
* Concrete algorithms call this during construction to declare support for
* specific roles (e.g., {@code ENCRYPT}, {@code VERIFY}). When
* {@link #createContext(KeyUsage, Key, ContextSpec)} is later invoked, the provided
* {@code key} and optional {@code spec} are matched against these bindings.
* {@link #createContext(KeyUsage, Key, ContextSpec)} is later invoked, the
* provided {@code key} and optional {@code spec} are matched against these
* bindings.
* </p>
*
* @param role supported {@link KeyUsage} role
@@ -397,8 +393,7 @@ public abstract class CryptoAlgorithm { // NOPMD
if (rb.accepts(key, spec)) {
S resolved = (spec != null) ? spec
: Objects.requireNonNull(rb.defaultSpec.get(), "defaultSpec value must not be null");
C ctx = Objects.requireNonNull(rb.factory.createContext(key, resolved),
_id + " factory returned null");
C ctx = Objects.requireNonNull(rb.factory.createContext(key, resolved), _id + " factory returned null");
// Enforce the declared context type contract:
if (!rb.ctxType.isInstance(ctx)) {
throw new IllegalStateException(_id + " factory returned " + ctx.getClass().getName()
@@ -411,8 +406,7 @@ public abstract class CryptoAlgorithm { // NOPMD
+ (spec == null ? " (default spec)" : " and spec=" + spec.getClass().getName()));
}
private <S extends AlgorithmKeySpec> S resolveDefault(Class<S> specType,
Supplier<? extends S> defaultSpecOrNull) {
private <S extends AlgorithmKeySpec> S resolveDefault(Class<S> specType, Supplier<? extends S> defaultSpecOrNull) {
if (defaultSpecOrNull == null) {
return null;
}
@@ -426,16 +420,18 @@ public abstract class CryptoAlgorithm { // NOPMD
/**
* Registers asymmetric key-pair generation for one exact specification class.
*
* <p>The optional default is resolved and validated during registration.
* <p>
* The optional default is resolved and validated during registration.
* Registered generators must be safe for concurrent invocation after the
* algorithm is published.</p>
* algorithm is published.
* </p>
*
* @param specType exact specification class
* @param generator non-null generator
* @param specType exact specification class
* @param generator non-null generator
* @param defaultSpecOrNull optional default supplier, evaluated once
* @param <S> specification type
* @throws NullPointerException if a required argument or supplied default is
* {@code null}
* @param <S> specification type
* @throws NullPointerException if a required argument or supplied default
* is {@code null}
* @throws IllegalArgumentException if the supplied default has the wrong type
*/
protected final <S extends AlgorithmKeySpec> void registerAsymmetricKeyPairGenerator(Class<S> specType,
@@ -450,7 +446,7 @@ public abstract class CryptoAlgorithm { // NOPMD
*
* @param specType exact specification class
* @param importer non-null importer safe for concurrent invocation
* @param <S> specification type
* @param <S> specification type
* @throws NullPointerException if an argument is {@code null}
*/
protected final <S extends AlgorithmKeySpec> void registerPublicKeyImporter(Class<S> specType,
@@ -464,7 +460,7 @@ public abstract class CryptoAlgorithm { // NOPMD
*
* @param specType exact specification class
* @param importer non-null importer safe for concurrent invocation
* @param <S> specification type
* @param <S> specification type
* @throws NullPointerException if an argument is {@code null}
*/
protected final <S extends AlgorithmKeySpec> void registerPrivateKeyImporter(Class<S> specType,
@@ -476,14 +472,16 @@ public abstract class CryptoAlgorithm { // NOPMD
/**
* Registers symmetric-key generation for one exact specification class.
*
* <p>The optional default is resolved and validated during registration.</p>
* <p>
* The optional default is resolved and validated during registration.
* </p>
*
* @param specType exact specification class
* @param generator non-null generator safe for concurrent invocation
* @param specType exact specification class
* @param generator non-null generator safe for concurrent invocation
* @param defaultSpecOrNull optional default supplier, evaluated once
* @param <S> specification type
* @throws NullPointerException if a required argument or supplied default is
* {@code null}
* @param <S> specification type
* @throws NullPointerException if a required argument or supplied default
* is {@code null}
* @throws IllegalArgumentException if the supplied default has the wrong type
*/
protected final <S extends AlgorithmKeySpec> void registerSymmetricKeyGenerator(Class<S> specType,
@@ -498,7 +496,7 @@ public abstract class CryptoAlgorithm { // NOPMD
*
* @param specType exact specification class
* @param importer non-null importer safe for concurrent invocation
* @param <S> specification type
* @param <S> specification type
* @throws NullPointerException if an argument is {@code null}
*/
protected final <S extends AlgorithmKeySpec> void registerSymmetricKeyImporter(Class<S> specType,
@@ -515,12 +513,14 @@ public abstract class CryptoAlgorithm { // NOPMD
* Returns the asymmetric key-pair generator registered for an exact
* specification class.
*
* <p>The returned implementation may be shared and invoked concurrently.</p>
* <p>
* The returned implementation may be shared and invoked concurrently.
* </p>
*
* @param specType exact specification class; subclasses are not matched
* @param <S> specification type
* @param <S> specification type
* @return registered generator
* @throws NullPointerException if {@code specType} is {@code null}
* @throws NullPointerException if {@code specType} is {@code null}
* @throws IllegalArgumentException if no generator is registered
*/
@SuppressWarnings("unchecked")
@@ -537,12 +537,14 @@ public abstract class CryptoAlgorithm { // NOPMD
/**
* Returns the public-key importer registered for an exact specification class.
*
* <p>The returned implementation may be shared and invoked concurrently.</p>
* <p>
* The returned implementation may be shared and invoked concurrently.
* </p>
*
* @param specType exact specification class; subclasses are not matched
* @param <S> specification type
* @param <S> specification type
* @return registered importer
* @throws NullPointerException if {@code specType} is {@code null}
* @throws NullPointerException if {@code specType} is {@code null}
* @throws IllegalArgumentException if no importer is registered
*/
@SuppressWarnings("unchecked")
@@ -556,15 +558,16 @@ public abstract class CryptoAlgorithm { // NOPMD
}
/**
* Returns the private-key importer registered for an exact specification
* class.
* Returns the private-key importer registered for an exact specification class.
*
* <p>The returned implementation may be shared and invoked concurrently.</p>
* <p>
* The returned implementation may be shared and invoked concurrently.
* </p>
*
* @param specType exact specification class; subclasses are not matched
* @param <S> specification type
* @param <S> specification type
* @return registered importer
* @throws NullPointerException if {@code specType} is {@code null}
* @throws NullPointerException if {@code specType} is {@code null}
* @throws IllegalArgumentException if no importer is registered
*/
@SuppressWarnings("unchecked")
@@ -581,12 +584,14 @@ public abstract class CryptoAlgorithm { // NOPMD
* Returns the symmetric-key generator registered for an exact specification
* class.
*
* <p>The returned implementation may be shared and invoked concurrently.</p>
* <p>
* The returned implementation may be shared and invoked concurrently.
* </p>
*
* @param specType exact specification class; subclasses are not matched
* @param <S> specification type
* @param <S> specification type
* @return registered generator
* @throws NullPointerException if {@code specType} is {@code null}
* @throws NullPointerException if {@code specType} is {@code null}
* @throws IllegalArgumentException if no generator is registered
*/
@SuppressWarnings("unchecked")
@@ -603,12 +608,14 @@ public abstract class CryptoAlgorithm { // NOPMD
* Returns the symmetric-key importer registered for an exact specification
* class.
*
* <p>The returned implementation may be shared and invoked concurrently.</p>
* <p>
* The returned implementation may be shared and invoked concurrently.
* </p>
*
* @param specType exact specification class; subclasses are not matched
* @param <S> specification type
* @param <S> specification type
* @return registered importer
* @throws NullPointerException if {@code specType} is {@code null}
* @throws NullPointerException if {@code specType} is {@code null}
* @throws IllegalArgumentException if no importer is registered
*/
@SuppressWarnings("unchecked")
@@ -628,14 +635,12 @@ public abstract class CryptoAlgorithm { // NOPMD
*/
public final List<KeyOperationInfo> keyOperations() {
List<KeyOperationInfo> result = new ArrayList<>();
addOperationInfo(result, KeyOperation.ASYMMETRIC_KEY_PAIR_GENERATE, keyPairGenerators,
asymmetricDefaults);
addOperationInfo(result, KeyOperation.ASYMMETRIC_KEY_PAIR_GENERATE, keyPairGenerators, asymmetricDefaults);
addOperationInfo(result, KeyOperation.ASYMMETRIC_PUBLIC_IMPORT, publicKeyImporters, Map.of());
addOperationInfo(result, KeyOperation.ASYMMETRIC_PRIVATE_IMPORT, privateKeyImporters, Map.of());
addOperationInfo(result, KeyOperation.SYMMETRIC_GENERATE, symmetricKeyGenerators, symmetricDefaults);
addOperationInfo(result, KeyOperation.SYMMETRIC_IMPORT, symmetricKeyImporters, Map.of());
result.sort(Comparator.comparing(KeyOperationInfo::operation)
.thenComparing(info -> info.specType().getName()));
result.sort(Comparator.comparing(KeyOperationInfo::operation).thenComparing(info -> info.specType().getName()));
return List.copyOf(result);
}

View File

@@ -17,10 +17,12 @@ import java.util.TreeMap;
/**
* Immutable registry of {@link CryptoAlgorithm} providers.
*
* <p>Providers are discovered once through {@link ServiceLoader}, sorted by
* <p>
* Providers are discovered once through {@link ServiceLoader}, sorted by
* canonical algorithm identifier, and retained in one immutable registry.
* Runtime policy and auditing belong exclusively to explicitly created
* {@link zeroecho.sdk.ZeroEchoSession} instances.</p>
* {@link zeroecho.sdk.ZeroEchoSession} instances.
* </p>
*
* @since 1.0
*/

View File

@@ -93,9 +93,9 @@ public final class CryptoCatalog {
* {@link CryptoAlgorithms}.
*
* <p>
* Provider discovery, deterministic ordering, and duplicate checking occur
* once in {@code CryptoAlgorithms}. This method neither scans providers nor
* copies their collection.
* Provider discovery, deterministic ordering, and duplicate checking occur once
* in {@code CryptoAlgorithms}. This method neither scans providers nor copies
* their collection.
* </p>
*
* @return an immutable {@code CryptoCatalog} with all discovered algorithms
@@ -158,40 +158,16 @@ public final class CryptoCatalog {
/**
* Serializes the catalog to a compact JSON document.
*
* <p>
* The schema is:
* </p>
* <pre>{@code
* {
* "algorithms": [
* {
* "id": "AES/GCM",
* "displayName": "AES-GCM",
* "capabilities": [
* {
* "family": "SYMMETRIC",
* "role": "ENCRYPT",
* "contextType": "AeadEncryptContext",
* "keyType": "SecretKey",
* "specType": "AeadSpec",
* "defaultSpec": "Random nonce, 128-bit tag"
* }
* ],
* "asymmetricKeyBuilders": [
* { "specType": "Ed25519Spec", "defaultKeySpec": "Ed25519 default" }
* ],
* "symmetricKeyBuilders": [
* { "specType": "AesKeySpec", "defaultKeySpec": "AES-256" }
* ]
* }
* ]
* }
* }</pre>
* <p> The schema is: </p> <pre>{@code { "algorithms": [ { "id": "AES/GCM",
* "displayName": "AES-GCM", "capabilities": [ { "family": "SYMMETRIC", "role":
* "ENCRYPT", "contextType": "AeadEncryptContext", "keyType": "SecretKey",
* "specType": "AeadSpec", "defaultSpec": "Random nonce, 128-bit tag" } ],
* "asymmetricKeyBuilders": [ { "specType": "Ed25519Spec", "defaultKeySpec":
* "Ed25519 default" } ], "symmetricKeyBuilders": [ { "specType": "AesKeySpec",
* "defaultKeySpec": "AES-256" } ] } ] } }</pre>
*
* <p>
* String values are escaped for quotes and backslashes. The method does not
* attempt to pretty-print; callers can format the output if needed.
* </p>
* <p> String values are escaped for quotes and backslashes. The method does not
* attempt to pretty-print; callers can format the output if needed. </p>
*
* @return a JSON string describing algorithms, capabilities, and key builders
*/
@@ -230,8 +206,7 @@ public final class CryptoCatalog {
}
firstOperation = false;
sb.append('{').append(jsonField("operation", operation.operation().name())).append(',')
.append(jsonField("specType", operation.specType().getSimpleName()))
.append(",\"defaultSpec\":")
.append(jsonField("specType", operation.specType().getSimpleName())).append(",\"defaultSpec\":")
.append(operation.defaultSpec() == null ? "null" : jsonString(labelOf(operation.defaultSpec())))
.append('}');
}
@@ -273,9 +248,8 @@ public final class CryptoCatalog {
}
sb.append("</capabilities><keyOperations>");
for (KeyOperationInfo operation : a.keyOperations()) {
sb.append("<keyOperation operation=\"").append(operation.operation().name())
.append("\" specType=\"").append(esc(operation.specType().getSimpleName()))
.append("\"><defaultSpec>")
sb.append("<keyOperation operation=\"").append(operation.operation().name()).append("\" specType=\"")
.append(esc(operation.specType().getSimpleName())).append("\"><defaultSpec>")
.append(operation.defaultSpec() == null ? "" : esc(labelOf(operation.defaultSpec())))
.append("</defaultSpec></keyOperation>");
}

View File

@@ -14,20 +14,20 @@ import zeroecho.core.spec.AlgorithmKeySpec;
/**
* Immutable metadata for one exact key operation.
*
* @param operation operation guaranteed by the associated lookup
* @param specType exact accepted specification type
* @param operation operation guaranteed by the associated lookup
* @param specType exact accepted specification type
* @param defaultSpec resolved generation default, or {@code null} for import
* operations and generators without a default
* @since 1.0
*/
public record KeyOperationInfo(KeyOperation operation,
Class<? extends AlgorithmKeySpec> specType, AlgorithmKeySpec defaultSpec) {
public record KeyOperationInfo(KeyOperation operation, Class<? extends AlgorithmKeySpec> specType,
AlgorithmKeySpec defaultSpec) {
/**
* Validates the metadata invariant.
*
* @throws NullPointerException if {@code operation} or {@code specType} is
* {@code null}
* @throws NullPointerException if {@code operation} or {@code specType} is
* {@code null}
* @throws IllegalArgumentException if a default is incompatible with
* {@code specType}, or an import operation
* declares a default
@@ -38,9 +38,9 @@ public record KeyOperationInfo(KeyOperation operation,
if (defaultSpec != null && !specType.isInstance(defaultSpec)) {
throw new IllegalArgumentException("defaultSpec must be an instance of " + specType.getName());
}
if (defaultSpec != null && (operation == KeyOperation.SYMMETRIC_IMPORT
|| operation == KeyOperation.ASYMMETRIC_PUBLIC_IMPORT
|| operation == KeyOperation.ASYMMETRIC_PRIVATE_IMPORT)) {
if (defaultSpec != null
&& (operation == KeyOperation.SYMMETRIC_IMPORT || operation == KeyOperation.ASYMMETRIC_PUBLIC_IMPORT
|| operation == KeyOperation.ASYMMETRIC_PRIVATE_IMPORT)) {
throw new IllegalArgumentException("import operations cannot declare a default specification");
}
}

View File

@@ -147,8 +147,8 @@ public abstract class AbstractCryptoAlgorithm extends CryptoAlgorithm {
*
* <h4>Validation</h4> Type checks happen at creation time (via {@code bind})
* and again when
* {@link CryptoAlgorithm#createContext(KeyUsage, Key, ContextSpec)} is
* called. If a factory returns a context not assignable to {@code ctxType}, an
* {@link CryptoAlgorithm#createContext(KeyUsage, Key, ContextSpec)} is called.
* If a factory returns a context not assignable to {@code ctxType}, an
* {@link IllegalStateException} will be thrown.
*
* @param family high-level algorithm family classification

View File

@@ -63,9 +63,10 @@ import zeroecho.core.util.RandomSupport;
*
* <p>
* IV and optional AAD are exchanged via a {@link conflux.CtxInterface} set with
* {@link #setContext(conflux.CtxInterface)}. Encryption always generates a fresh
* IV after atomically claiming the context; a caller-provided IV is never used
* for encryption. Decryption requires the IV from the context or encoded header.
* {@link #setContext(conflux.CtxInterface)}. Encryption always generates a
* fresh IV after atomically claiming the context; a caller-provided IV is never
* used for encryption. Decryption requires the IV from the context or encoded
* header.
* </p>
*
* <p>
@@ -311,10 +312,7 @@ public final class AesCipherContext implements EncryptionContext, ContextAware {
/** Single-use encryption lifecycle states. */
private enum OperationState {
NEW,
ENCRYPTING,
COMPLETED,
FAILED
NEW, ENCRYPTING, COMPLETED, FAILED
}
/** Marks the owning encryption context terminal as its stream is consumed. */

View File

@@ -60,8 +60,8 @@ import zeroecho.core.spec.AlgorithmKeySpec;
* </p>
*
* <p>
* Objects of this type are thread-safe while active and may be destroyed to wipe
* their owned key bytes. Access and marshalling fail after destruction.
* Objects of this type are thread-safe while active and may be destroyed to
* wipe their owned key bytes. Access and marshalling fail after destruction.
* </p>
*
* @since 1.0

View File

@@ -243,8 +243,8 @@ abstract class AbstractChaChaCipherContext<S extends ChaChaBaseSpec> implements
* Ensures a nonce is available in the context.
*
* <ul>
* <li>For encryption, always generates a new nonce after the context has
* been atomically claimed and stores a copy in the context.</li>
* <li>For encryption, always generates a new nonce after the context has been
* atomically claimed and stores a copy in the context.</li>
* <li>For decryption, validates presence and correct length.</li>
* </ul>
*
@@ -283,10 +283,7 @@ abstract class AbstractChaChaCipherContext<S extends ChaChaBaseSpec> implements
/** Single-use encryption lifecycle states. */
private enum OperationState {
NEW,
ENCRYPTING,
COMPLETED,
FAILED
NEW, ENCRYPTING, COMPLETED, FAILED
}
/** Marks the owning encryption context terminal as its stream is consumed. */

View File

@@ -34,6 +34,7 @@
package zeroecho.core.alg.chacha;
import zeroecho.core.util.RandomSupport;
/**
* <h2>ChaCha20 (stream) algorithm</h2>
*

View File

@@ -39,10 +39,10 @@
* the stream cipher ChaCha20 and the AEAD construction ChaCha20-Poly1305. The
* module contains algorithm descriptors, streaming cipher contexts,
* configuration specifications, optional header codecs for runtime parameters,
* and symmetric key import/generation specifications. Key import
* specifications are destroyable. The design favors safe defaults
* (12-byte nonces, 128-bit AEAD tag), explicit role-to-context binding, and a
* clear separation between static configuration and per-operation parameters.
* and symmetric key import/generation specifications. Key import specifications
* are destroyable. The design favors safe defaults (12-byte nonces, 128-bit
* AEAD tag), explicit role-to-context binding, and a clear separation between
* static configuration and per-operation parameters.
* </p>
*
* <h2>Components</h2>

View File

@@ -44,9 +44,9 @@ import zeroecho.core.context.AgreementContext;
* <h2>Generic JCA-based Key Agreement Context</h2>
*
* An {@link AgreementContext} backed by the standard JCA key-agreement API.
* This class supports elliptic-curve and modern Diffie-Hellman variants
* such as ECDH, XDH (X25519, X448), and others provided by the runtime or
* configured provider.
* This class supports elliptic-curve and modern Diffie-Hellman variants such as
* ECDH, XDH (X25519, X448), and others provided by the runtime or configured
* provider.
*
* <p>
* Instances of this context are created with a local {@link PrivateKey}, and

View File

@@ -99,9 +99,9 @@
* reconstructs public keys from X.509 encodings via
* {@link java.security.KeyFactory}.</li>
* <li><b>Signature contexts:</b>
* {@link zeroecho.core.alg.common.eddsa.CommonEdDSASignatureContext}
* delegates all operations to a generic JCA-backed signature adapter, enforcing
* a fixed tag length for the selected EdDSA variant.</li>
* {@link zeroecho.core.alg.common.eddsa.CommonEdDSASignatureContext} delegates
* all operations to a generic JCA-backed signature adapter, enforcing a fixed
* tag length for the selected EdDSA variant.</li>
* </ul>
*
* <h2>Design notes</h2>

View File

@@ -116,7 +116,8 @@ public final class SignatureInteropProfile { // NOPMD
* @param keyAlgorithmId ZeroEcho key algorithm identifier used for key
* import and matching, such as {@code RSA}
* @param contextAlgorithmId ZeroEcho context algorithm identifier used
* with {@code ZeroEchoSession.createContext(...)}
* with
* {@code ZeroEchoSession.createContext(...)}
* @param contextSpec explicit ZeroEcho context specification
* @param signatureRepresentation signature representation bridge between
* external bytes and internal ZeroEcho bytes

View File

@@ -60,9 +60,9 @@
* configured {@link java.security.Signature}, resolves a fixed tag length (via
* resolvers), and exposes a one-shot {@code wrap(InputStream)} API.
* Verification behavior is controlled by a pluggable comparison approach.</li>
* <li><b>SignatureStream</b> - internal passthrough input stream that feeds chunks to
* the signature engine, emits the trailer in SIGN mode, and performs final
* verification in VERIFY mode.</li>
* <li><b>SignatureStream</b> - internal passthrough input stream that feeds
* chunks to the signature engine, emits the trailer in SIGN mode, and performs
* final verification in VERIFY mode.</li>
* </ul>
*
* <h2>Length resolution</h2>

View File

@@ -117,8 +117,8 @@ public final class Sha2Sha3Algorithm extends AbstractCryptoAlgorithm {
MessageDigest md = MessageDigest.getInstance(s.algorithm().jca());
return new JcaDigestContext(this, md, s);
} catch (GeneralSecurityException e) {
throw new ProviderFailureException(
"Failed to initialize MessageDigest " + s.algorithm().jca(), e);
throw new ProviderFailureException("Failed to initialize MessageDigest " + s.algorithm().jca(),
e);
}
}, DigestSpec::sha256 // default for catalog/tests
);

View File

@@ -160,8 +160,7 @@ public final class EcdhAlgorithm extends AbstractCryptoAlgorithm {
() -> EcdsaCurveSpec.P256);
// Reuse EC builders/importers
registerAsymmetricKeyPairGenerator(EcdhCurveSpec.class, new EcdhKeyGenBuilder(),
() -> EcdhCurveSpec.P256);
registerAsymmetricKeyPairGenerator(EcdhCurveSpec.class, new EcdhKeyGenBuilder(), () -> EcdhCurveSpec.P256);
registerPublicKeyImporter(EcdsaPublicKeySpec.class, new EcdsaPublicKeyBuilder());
registerPrivateKeyImporter(EcdsaPrivateKeySpec.class, new EcdsaPrivateKeyBuilder());
}

View File

@@ -45,8 +45,8 @@ import zeroecho.core.spi.AsymmetricKeyPairGenerator;
/**
* <h2>ECDH Key Pair Generator</h2>
*
* Implementation of {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} for elliptic curve
* Diffie-Hellman (ECDH) key pairs.
* Implementation of {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} for
* elliptic curve Diffie-Hellman (ECDH) key pairs.
*
* <p>
* This builder generates fresh EC key pairs suitable for ECDH key agreement. It

View File

@@ -103,8 +103,8 @@ public final class EcdsaAlgorithm extends AbstractCryptoAlgorithm {
* <p>
* On construction, the algorithm declares its supported roles and registers
* builders with the {@link CryptoAlgorithm} infrastructure so they can be
* discovered by the {@link CryptoCatalog} or invoked through the
* session-bound {@link zeroecho.sdk.KeyBuilders} entry point.
* discovered by the {@link CryptoCatalog} or invoked through the session-bound
* {@link zeroecho.sdk.KeyBuilders} entry point.
* </p>
*/
public EcdsaAlgorithm() {
@@ -134,8 +134,7 @@ public final class EcdsaAlgorithm extends AbstractCryptoAlgorithm {
}
}, () -> EcdsaCurveSpec.P256);
registerAsymmetricKeyPairGenerator(EcdsaCurveSpec.class, new EcdsaKeyGenBuilder(),
() -> EcdsaCurveSpec.P256);
registerAsymmetricKeyPairGenerator(EcdsaCurveSpec.class, new EcdsaKeyGenBuilder(), () -> EcdsaCurveSpec.P256);
registerPublicKeyImporter(EcdsaPublicKeySpec.class, new EcdsaPublicKeyBuilder());
registerPrivateKeyImporter(EcdsaPrivateKeySpec.class, new EcdsaPrivateKeyBuilder());
}

View File

@@ -45,14 +45,14 @@ import zeroecho.core.spi.AsymmetricKeyPairGenerator;
* <h2>ECDSA Key Pair Generator</h2>
*
* Implementation of {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} for
* {@link EcdsaCurveSpec}.
* This builder is responsible for generating new elliptic curve key pairs for
* use with the {@link EcdsaAlgorithm}.
* {@link EcdsaCurveSpec}. This builder is responsible for generating new
* elliptic curve key pairs for use with the {@link EcdsaAlgorithm}.
*
* <p>The exact supported operation is
* {@link #generateKeyPair(EcdsaCurveSpec)}. Public and private import are
* registered separately through {@link EcdsaPublicKeyBuilder} and
* {@link EcdsaPrivateKeyBuilder}.</p>
* <p>
* The exact supported operation is {@link #generateKeyPair(EcdsaCurveSpec)}.
* Public and private import are registered separately through
* {@link EcdsaPublicKeyBuilder} and {@link EcdsaPrivateKeyBuilder}.
* </p>
*
* <h2>Usage</h2> Typically accessed through the session key-operation API or
* {@link CryptoAlgorithm#asymmetricKeyPairGenerator(Class)}.

View File

@@ -49,9 +49,11 @@ import zeroecho.core.spi.PrivateKeyImporter;
* {@link EcdsaPrivateKeySpec}. This builder is responsible for importing ECDSA
* private keys from encoded representations.
*
* <p>The exact supported operation is
* {@link #importPrivate(EcdsaPrivateKeySpec)}. Generation and public import are
* registered through their own operation-specific implementations.</p>
* <p>
* The exact supported operation is {@link #importPrivate(EcdsaPrivateKeySpec)}.
* Generation and public import are registered through their own
* operation-specific implementations.
* </p>
*
* <h2>Encoding</h2> The {@link EcdsaPrivateKeySpec} stores the private key in
* PKCS#8 DER format. This builder delegates to a JCA {@link KeyFactory} for the

View File

@@ -48,9 +48,11 @@ import zeroecho.core.spi.PublicKeyImporter;
* {@link EcdsaPublicKeySpec}. This builder is responsible for importing ECDSA
* public keys from X.509 SubjectPublicKeyInfo encodings.
*
* <p>The exact supported operation is
* {@link #importPublic(EcdsaPublicKeySpec)}. Generation and private import are
* registered through their own operation-specific implementations.</p>
* <p>
* The exact supported operation is {@link #importPublic(EcdsaPublicKeySpec)}.
* Generation and private import are registered through their own
* operation-specific implementations.
* </p>
*
* <h2>Encoding</h2> The {@link EcdsaPublicKeySpec} stores the public key in
* standard X.509 DER format. This builder delegates to a JCA {@link KeyFactory}

View File

@@ -38,8 +38,8 @@ import zeroecho.core.alg.common.eddsa.AbstractEdDSAKeyGenBuilder;
/**
* <h2>Key-pair builder for Ed25519</h2>
*
* Concrete {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} implementation for
* generating Ed25519 key pairs.
* Concrete {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} implementation
* for generating Ed25519 key pairs.
*
* <p>
* This builder delegates to the JCA provider under the canonical algorithm name

View File

@@ -38,8 +38,8 @@ import zeroecho.core.alg.common.eddsa.AbstractEncodedPrivateKeyBuilder;
/**
* <h2>Private key builder for Ed25519</h2>
*
* Concrete {@link zeroecho.core.spi.PrivateKeyImporter} for importing
* wrapping Ed25519 private keys.
* Concrete {@link zeroecho.core.spi.PrivateKeyImporter} for importing wrapping
* Ed25519 private keys.
*
* <p>
* This builder integrates with the JCA under the canonical key factory

View File

@@ -38,8 +38,8 @@ import zeroecho.core.alg.common.eddsa.AbstractEncodedPublicKeyBuilder;
/**
* <h2>Public key builder for Ed25519</h2>
*
* Concrete {@link zeroecho.core.spi.PublicKeyImporter} for importing
* wrapping Ed25519 public keys.
* Concrete {@link zeroecho.core.spi.PublicKeyImporter} for importing wrapping
* Ed25519 public keys.
*
* <p>
* This builder integrates with the JCA under the canonical key factory

View File

@@ -66,8 +66,7 @@ import zeroecho.core.spec.AlgorithmKeySpec;
*
* <h2>Thread-safety</h2> Stateless and safe for concurrent use. Each call to
* {@link zeroecho.core.spi.PrivateKeyImporter#importPrivate(AlgorithmKeySpec)}
* creates a new
* {@link java.security.KeyFactory}.
* creates a new {@link java.security.KeyFactory}.
*
* @since 1.0
*/

View File

@@ -65,8 +65,7 @@ import zeroecho.core.spec.AlgorithmKeySpec;
*
* <h2>Thread-safety</h2> Stateless and safe for concurrent use. Each call to
* {@link zeroecho.core.spi.PublicKeyImporter#importPublic(AlgorithmKeySpec)}
* creates a new
* {@link java.security.KeyFactory}.
* creates a new {@link java.security.KeyFactory}.
*
* @since 1.0
*/

View File

@@ -63,7 +63,8 @@ import zeroecho.core.spec.AlgorithmKeySpec;
* </p>
*
* @see KyberAlgorithm
* @see zeroecho.sdk.KeyBuilders.Asymmetric#generateKeyPair(String, AlgorithmKeySpec)
* @see zeroecho.sdk.KeyBuilders.Asymmetric#generateKeyPair(String,
* AlgorithmKeySpec)
*/
public final class KyberKeyGenSpec implements AlgorithmKeySpec, Describable {
/**

View File

@@ -47,9 +47,9 @@ import zeroecho.core.spec.AlgorithmKeySpec;
* Specification wrapper for a Kyber (ML-KEM) private key encoded in PKCS#8.
*
* <p>
* Instances of this class carry an owned copy of the PKCS#8-encoded private
* key bytes. They are used with {@link zeroecho.core.CryptoAlgorithm} key
* builders to import keys into the providers native representation.
* Instances of this class carry an owned copy of the PKCS#8-encoded private key
* bytes. They are used with {@link zeroecho.core.CryptoAlgorithm} key builders
* to import keys into the providers native representation.
* </p>
*
* <h2>Encoding</h2>

View File

@@ -102,8 +102,7 @@ public record BlockGeometry(int inChunkSize, int outChunkSize, int finalizationO
"inChunkSize must not exceed outChunkSize: " + inChunkSize + " > " + outChunkSize);
}
if (finalizationOutputChunks != 0) {
throw new IllegalArgumentException(
"finalizationOutputChunks must be zero: " + finalizationOutputChunks);
throw new IllegalArgumentException("finalizationOutputChunks must be zero: " + finalizationOutputChunks);
}
}

View File

@@ -43,7 +43,8 @@ import zeroecho.core.spec.AlgorithmKeySpec;
* A {@code SaberKeyGenSpec} selects one of the SABER parameter variants
* standardized in round-3 submissions. Each variant balances performance,
* bandwidth, and security level. This spec is passed to a registered
* {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} to generate a SABER key pair.
* {@link zeroecho.core.spi.AsymmetricKeyPairGenerator} to generate a SABER key
* pair.
* </p>
*
* <h2>Variants</h2> The {@link Variant} enumeration identifies supported SABER

View File

@@ -54,10 +54,12 @@ import zeroecho.core.spi.AsymmetricKeyPairGenerator;
* Reflection is used to avoid a hard dependency on all parameter variants.
* </p>
*
* <p>The exact supported operation is
* {@link #generateKeyPair(SphincsPlusKeyGenSpec)}. Public and private import are
* registered separately for {@link SphincsPlusPublicKeySpec} and
* {@link SphincsPlusPrivateKeySpec}.</p>
* <p>
* The exact supported operation is
* {@link #generateKeyPair(SphincsPlusKeyGenSpec)}. Public and private import
* are registered separately for {@link SphincsPlusPublicKeySpec} and
* {@link SphincsPlusPrivateKeySpec}.
* </p>
*
* <h2>Example</h2> <pre>{@code
* SphincsPlusKeyGenSpec spec =

View File

@@ -51,9 +51,11 @@ import zeroecho.core.spi.PrivateKeyImporter;
* pairs but focuses solely on importing private key material.
* </p>
*
* <p>The exact supported operation is
* <p>
* The exact supported operation is
* {@link #importPrivate(SphincsPlusPrivateKeySpec)}. Other key operations are
* registered through their own exact interfaces.</p>
* registered through their own exact interfaces.
* </p>
*
* <h2>Example</h2> <pre>{@code
* // Assuming bytes contain a PKCS#8-encoded SPHINCS+ private key:

View File

@@ -48,7 +48,8 @@ import zeroecho.core.spec.AlgorithmKeySpec;
* <p>
* {@code SphincsPlusPrivateKeySpec} wraps a PKCS#8-encoded SPHINCS+ private key
* along with the provider name that should be used for import. It is a simple
* destroyable holder designed for use with {@link SphincsPlusPrivateKeyBuilder}.
* destroyable holder designed for use with
* {@link SphincsPlusPrivateKeyBuilder}.
* </p>
*
* <h2>Encoding</h2>

View File

@@ -50,9 +50,11 @@ import zeroecho.core.spi.PublicKeyImporter;
* pairs, but focuses solely on importing public key material.
* </p>
*
* <p>The exact supported operation is
* <p>
* The exact supported operation is
* {@link #importPublic(SphincsPlusPublicKeySpec)}. Other key operations are
* registered through their own exact interfaces.</p>
* registered through their own exact interfaces.
* </p>
*
* <h2>Example</h2> <pre>{@code
* // Assuming bytes contain an X.509-encoded SPHINCS+ public key:

View File

@@ -51,7 +51,8 @@ import zeroecho.core.spi.AsymmetricKeyPairGenerator;
* <h2>Design and scope</h2>
* <ul>
* <li><b>Generation only:</b> This implementation exposes only the exact
* key-pair generation capability; import operations are registered separately.</li>
* key-pair generation capability; import operations are registered
* separately.</li>
* <li><b>Provider resolution:</b> The default JCA provider selection is used.
* If a specific provider is required, supply or register one that exposes the
* requested XDH algorithm name.</li>

View File

@@ -13,10 +13,12 @@ import java.util.Objects;
/**
* Utilities for enforcing the best-effort audit-listener contract.
*
* <p>The returned listener suppresses listener failures without logging callback
* <p>
* The returned listener suppresses listener failures without logging callback
* arguments, because those arguments may refer to sensitive cryptographic
* objects. Cryptographic operation outcomes therefore never depend on an audit
* sink's availability.</p>
* sink's availability.
* </p>
*
* @since 1.0
*/
@@ -36,8 +38,8 @@ public final class AuditListeners {
AuditListener target = Objects.requireNonNull(listener, "listener");
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = contextLoader == null ? ClassLoader.getSystemClassLoader() : contextLoader;
return (AuditListener) Proxy.newProxyInstance(loader,
new Class<?>[] { AuditListener.class }, (proxy, method, arguments) -> {
return (AuditListener) Proxy.newProxyInstance(loader, new Class<?>[] { AuditListener.class },
(proxy, method, arguments) -> {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(target, arguments);
}

View File

@@ -10,8 +10,10 @@ package zeroecho.core.audit;
/**
* Defines the session-owned automatic auditing strategy.
*
* <p>Audit listener failures are best-effort diagnostics and never change the
* outcome of a cryptographic operation.</p>
* <p>
* Audit listener failures are best-effort diagnostics and never change the
* outcome of a cryptographic operation.
* </p>
*
* @since 1.0
*/

View File

@@ -105,8 +105,8 @@ import zeroecho.core.spec.ContextSpec;
* <li>Counting is performed by decorating the returned {@code InputStream}s; no
* buffering beyond normal {@code FilterInputStream} forwarding is
* introduced.</li>
* <li>Idempotent wrapping: contexts already wrapped by this utility are returned
* unchanged. Unrelated JDK proxies are wrapped normally.</li>
* <li>Idempotent wrapping: contexts already wrapped by this utility are
* returned unchanged. Unrelated JDK proxies are wrapped normally.</li>
* </ul>
*
* <h2>Usage example</h2> <pre>{@code
@@ -190,8 +190,7 @@ public final class AuditedContexts {
}
private static boolean isAuditedProxy(CryptoContext context) {
return Proxy.isProxyClass(context.getClass())
&& Proxy.getInvocationHandler(context) instanceof AuditingHandler;
return Proxy.isProxyClass(context.getClass()) && Proxy.getInvocationHandler(context) instanceof AuditingHandler;
}
@SuppressWarnings("unchecked")
@@ -242,8 +241,7 @@ public final class AuditedContexts {
}
safeAudit.onContextCreatedMeta(ctxId, algoId == null ? UNKNOWN : algoId,
provider == null ? UNKNOWN : provider,
role, keyFp, specMeta);
provider == null ? UNKNOWN : provider, role, keyFp, specMeta);
}
ClassLoader cl = ctx.getClass().getClassLoader(); // NOPMD
@@ -720,8 +718,7 @@ public final class AuditedContexts {
StringBuilder fingerprint = new StringBuilder(16);
for (int index = 0; index < Math.min(8, digest.length); index++) {
int value = digest[index] & 0xff;
fingerprint.append(Character.forDigit(value >>> 4, 16))
.append(Character.forDigit(value & 0x0f, 16));
fingerprint.append(Character.forDigit(value >>> 4, 16)).append(Character.forDigit(value & 0x0f, 16));
}
return key.getAlgorithm() + ":" + fingerprint;
} catch (NoSuchAlgorithmException exception) {

View File

@@ -204,9 +204,9 @@ public final class JulAuditListenerStd implements AuditListener {
* appends a stack trace in addition to the structured summary.
*
* <p>
* Stack traces may contain provider exception messages or application
* values. Enabling them is an explicit diagnostic opt-in and requires a
* suitably protected log destination.
* Stack traces may contain provider exception messages or application values.
* Enabling them is an explicit diagnostic opt-in and requires a suitably
* protected log destination.
* </p>
*
* @param include true to include stack traces, false to omit them
@@ -600,8 +600,7 @@ public final class JulAuditListenerStd implements AuditListener {
StringBuilder sb = new StringBuilder(key.getAlgorithm()).append(':');
for (int i = 0; i < Math.min(8, digest.length); i++) {
int value = digest[i] & 0xff;
sb.append(Character.forDigit(value >>> 4, 16))
.append(Character.forDigit(value & 0x0f, 16));
sb.append(Character.forDigit(value >>> 4, 16)).append(Character.forDigit(value & 0x0f, 16));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {

View File

@@ -46,13 +46,11 @@ package zeroecho.core.err;
* <h2>When it is thrown</h2>
* <ul>
* <li>During
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}
* after policy validation, if the resolved algorithm exposes no bindings for
* the given role.</li>
* <li>Directly from
* {@link zeroecho.core.CryptoAlgorithm#createContext(zeroecho.core.KeyUsage,
* java.security.Key, zeroecho.core.spec.ContextSpec)}
* {@link zeroecho.core.CryptoAlgorithm#createContext(zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}
* when no binding exists for the role.</li>
* </ul>
*

View File

@@ -157,8 +157,8 @@ public abstract class AbstractChunkTransformInputStream extends FilterInputStrea
* @param outChunkSize size of output chunks produced by the transform (must be
* > 0)
* @param chunks number of chunks buffered at once (must be > 0)
* @throws IllegalArgumentException if a size is outside its documented range
* or a buffer size overflows
* @throws IllegalArgumentException if a size is outside its documented range or
* a buffer size overflows
*/
protected AbstractChunkTransformInputStream(InputStream upstream, int inChunkSize, int outChunkSize, int chunks) {
super(upstream);
@@ -190,8 +190,8 @@ public abstract class AbstractChunkTransformInputStream extends FilterInputStrea
* steady-state (must be > 0)
* @param finalizationOutputChunks number of extra output chunks reserved for
* finalization (must be >= 0)
* @throws IllegalArgumentException if a size is outside its documented range
* or a buffer size overflows
* @throws IllegalArgumentException if a size is outside its documented range or
* a buffer size overflows
*/
protected AbstractChunkTransformInputStream(InputStream upstream, int inChunkSize, int outChunkSize, int chunks,
int finalizationOutputChunks) {
@@ -261,8 +261,7 @@ public abstract class AbstractChunkTransformInputStream extends FilterInputStrea
// EOF: run finalization exactly once, even if there's no remainder,
// and surface any produced bytes (e.g., padding block, GCM tag).
if (!eofSeen) {
int finalOut = validateOutputCount(doFinal(inBuf, 0, 0, outBuf, 0), outBuf.length,
"finalization");
int finalOut = validateOutputCount(doFinal(inBuf, 0, 0, outBuf, 0), outBuf.length, "finalization");
outPtr = 0;
outLen = finalOut;
eofSeen = true;
@@ -273,8 +272,7 @@ public abstract class AbstractChunkTransformInputStream extends FilterInputStrea
// all chunks are aligned to the specified boundary (inChunkSize) -> transform
// can be simply invoked
outLen = validateOutputCount(transform(inBuf, 0, inLen / inChunkSize, outBuf), outBuf.length,
"transformation");
outLen = validateOutputCount(transform(inBuf, 0, inLen / inChunkSize, outBuf), outBuf.length, "transformation");
outPtr = 0;
int left = inLen % inChunkSize;
@@ -291,8 +289,7 @@ public abstract class AbstractChunkTransformInputStream extends FilterInputStrea
return true;
}
private static void validateGeometry(int inChunkSize, int outChunkSize, int chunks,
int finalizationOutputChunks) {
private static void validateGeometry(int inChunkSize, int outChunkSize, int chunks, int finalizationOutputChunks) {
if (inChunkSize < MIN_INPUT_CHUNK_SIZE) {
throw new IllegalArgumentException("inChunkSize must be greater than 1");
}

View File

@@ -53,13 +53,12 @@ import javax.crypto.Cipher;
* block; a final partial block (if any) is processed by a single
* {@code doFinal}. This mode is restricted to RSA and ElGamal.</li>
* <li><b>Left-padded independent block stream</b> - like the independent block
* stream, but
* left-pads each transformed output block with zeros up to
* stream, but left-pads each transformed output block with zeros up to
* {@code outChunkSize}. Final blocks must be complete; otherwise an
* {@link IllegalStateException} is thrown.</li>
* <li><b>Continuous stream</b> - uses
* {@code Cipher.update(...)} for bulk bytes and a single {@code doFinal()} at
* end of stream. This is suitable for CTR/CFB/OFB/GCM and padding modes.</li>
* <li><b>Continuous stream</b> - uses {@code Cipher.update(...)} for bulk bytes
* and a single {@code doFinal()} at end of stream. This is suitable for
* CTR/CFB/OFB/GCM and padding modes.</li>
* </ul>
*
* <h2>Block sizing</h2>
@@ -344,7 +343,8 @@ public final class CipherTransformInputStreamBuilder {
* </p>
*
* @return a new InputStream that transforms bytes on the fly
* @throws NullPointerException if {@code upstream} or {@code cipher} is null
* @throws NullPointerException if {@code upstream} or {@code cipher} is
* null
* @throws IllegalArgumentException if independent-block processing is selected
* for an unsupported algorithm or buffer
* geometry is invalid

View File

@@ -58,6 +58,7 @@ final class SmartBlockStream extends AbstractChunkTransformInputStream {
private static final Logger LOG = Logger.getLogger(SmartBlockStream.class.getName());
private final Cipher cipher;
/* package */ SmartBlockStream(InputStream upstream, Cipher cipher, int inChunkSize, int outChunkSize,
int bufferedBlocks) {
super(upstream, inChunkSize, outChunkSize, bufferedBlocks);

View File

@@ -59,6 +59,7 @@ final class SmartPaddedBlockStream extends AbstractChunkTransformInputStream {
private static final Logger LOG = Logger.getLogger(SmartPaddedBlockStream.class.getName());
private final Cipher cipher;
/* package */ SmartPaddedBlockStream(InputStream upstream, Cipher cipher, int inChunkSize, int outChunkSize,
int bufferedBlocks) {
super(upstream, inChunkSize, outChunkSize, bufferedBlocks);

View File

@@ -54,12 +54,12 @@
* then calls {@code onCompleted()} exactly once at EOF.</li>
* <li>{@link CipherTransformInputStreamBuilder} - fluent builder that creates
* cipher-backed streams for RSA/ElGamal independent-block processing,
* left-zero-padded independent blocks, or
* continuous {@code update}+{@code doFinal} streaming.</li>
* left-zero-padded independent blocks, or continuous
* {@code update}+{@code doFinal} streaming.</li>
* <li>{@link SmartBlockStream}, {@link SmartPaddedBlockStream},
* {@link SmartContinuousBlockStream} - internal cipher-backed stream variants;
* the first two are restricted to independent RSA or ElGamal blocks
* used by the builder.</li>
* the first two are restricted to independent RSA or ElGamal blocks used by the
* builder.</li>
* <li>{@link TailStrippingInputStream} - withholds the last N bytes from the
* payload and delivers them to a callback at EOF (useful for tags, checksums,
* or footers).</li>

View File

@@ -61,8 +61,8 @@ import java.util.List;
*
* <h2>Serialization</h2>
* <ul>
* <li>{@link #writeTo(Appendable)} outputs each pair as {@code k=v\n}
* lines without escaping and reports checked I/O failures.</li>
* <li>{@link #writeTo(Appendable)} outputs each pair as {@code k=v\n} lines
* without escaping and reports checked I/O failures.</li>
* <li>{@link #readFrom(java.io.Reader)} parses lines in the same format,
* ignoring blank lines and comments starting with {@code #}.</li>
* </ul>
@@ -99,8 +99,7 @@ public final class PairSeq {
if (kv[elementIndex] == null) {
int pairIndex = elementIndex >>> 1;
String role = (elementIndex & 1) == 0 ? "key" : "value";
throw new IllegalArgumentException(
"pair " + pairIndex + " " + role + " must not be null");
throw new IllegalArgumentException("pair " + pairIndex + " " + role + " must not be null");
}
}
return new PairSeq(kv.clone());
@@ -201,8 +200,8 @@ public final class PairSeq {
}
/**
* Appends all pairs to the target as {@code key=value} lines, reporting
* checked I/O failures directly.
* Appends all pairs to the target as {@code key=value} lines, reporting checked
* I/O failures directly.
*
* <p>
* No escaping is performed; callers must ensure keys and values do not contain

View File

@@ -101,9 +101,9 @@ import java.util.function.Supplier;
* }</pre>
*
* <h2>Thread-safety</h2> Instances are immutable and thread-safe. Public
* accessors are resolved once per runtime class and operation type, then invoked
* through cached method handles. The unload-safe {@link ClassValue} caches do
* not retain otherwise unreachable class loaders.
* accessors are resolved once per runtime class and operation type, then
* invoked through cached method handles. The unload-safe {@link ClassValue}
* caches do not retain otherwise unreachable class loaders.
*
* @param <T> domain type that follows the marshalling and unmarshalling
* conventions
@@ -274,8 +274,7 @@ public final class PairSeqCodec<T> implements Codec<T, PairSeq> {
try {
Method method = runtimeType.getMethod("marshal");
if (!PairSeq.class.isAssignableFrom(method.getReturnType())) {
return new MarshalPlan(null,
"marshal() must return PairSeq in " + runtimeType.getName(), null);
return new MarshalPlan(null, "marshal() must return PairSeq in " + runtimeType.getName(), null);
}
MethodHandle handle = MethodHandles.lookup().unreflect(method);
return new MarshalPlan(handle, null, null);
@@ -327,14 +326,14 @@ public final class PairSeqCodec<T> implements Codec<T, PairSeq> {
"static unmarshal(PairSeq) must return " + runtimeType.getName(), null);
}
MethodHandle handle = MethodHandles.lookup().unreflect(method);
return new UnmarshalPlan(handle,
"static unmarshal(PairSeq) failed for " + runtimeType.getName(), null, null);
return new UnmarshalPlan(handle, "static unmarshal(PairSeq) failed for " + runtimeType.getName(),
null, null);
}
} catch (NoSuchMethodException ignored) {
// Resolve the constructor fallback below.
} catch (IllegalAccessException exception) {
return new UnmarshalPlan(null, null,
"static unmarshal(PairSeq) failed for " + runtimeType.getName(), exception);
return new UnmarshalPlan(null, null, "static unmarshal(PairSeq) failed for " + runtimeType.getName(),
exception);
}
try {

View File

@@ -33,7 +33,6 @@
******************************************************************************/
package zeroecho.core.spec;
/**
* Marker interface for algorithm-specific key specifications.
* <p>

View File

@@ -11,7 +11,8 @@ import zeroecho.core.spec.AlgorithmKeySpec;
/**
* Generates asymmetric key pairs for one exact specification type.
* Implementations must be stateless or otherwise safe for concurrent invocation.
* Implementations must be stateless or otherwise safe for concurrent
* invocation.
*
* @param <S> specification type
* @since 1.0

View File

@@ -15,11 +15,13 @@ import zeroecho.core.spec.ContextSpec;
/**
* Creates a cryptographic context from a key and a context specification.
*
* <p>Implementations report provider and parameter failures with unchecked
* <p>
* Implementations report provider and parameter failures with unchecked
* exceptions; context construction is a pure in-memory operation and does not
* expose an I/O failure contract. Factories must be stateless or otherwise safe
* for concurrent invocation; returned contexts retain their own documented
* thread-safety contracts.</p>
* thread-safety contracts.
* </p>
*
* @param <C> context type produced
* @param <K> key type accepted
@@ -31,7 +33,7 @@ public interface ContextFactoryKS<C extends CryptoContext, K extends Key, S exte
/**
* Creates a context bound to the supplied key and specification.
*
* @param key non-null key
* @param key non-null key
* @param spec non-null resolved context specification
* @return a newly created context
*/

View File

@@ -11,11 +11,13 @@ import zeroecho.core.storage.KeyringPassword;
/**
* Supplies a fresh destroyable password for one keyring open operation.
*
* <p>Ownership of the returned object transfers to the receiver, which must
* <p>
* Ownership of the returned object transfers to the receiver, which must
* destroy it in a {@code finally} block immediately after the keyring has been
* opened. Implementations must not source passwords from immutable strings,
* process arguments, system properties, environment fallbacks, persistent
* files, or global mutable state.</p>
* files, or global mutable state.
* </p>
*/
@FunctionalInterface
public interface KeyringUnlockProvider {

View File

@@ -10,8 +10,8 @@ import java.security.PrivateKey;
import zeroecho.core.spec.AlgorithmKeySpec;
/**
* Imports private keys for one exact specification type. Implementations must be
* stateless or otherwise safe for concurrent invocation.
* Imports private keys for one exact specification type. Implementations must
* be stateless or otherwise safe for concurrent invocation.
*
* @param <S> specification type
* @since 1.0

View File

@@ -34,20 +34,26 @@
/**
* Provider contracts for context construction and exact key operations.
*
* <p>Algorithms bind each supported role to a {@link ContextFactoryKS}. Context
* <p>
* Algorithms bind each supported role to a {@link ContextFactoryKS}. Context
* construction is an in-memory operation; stream attachment and processing are
* responsible for reporting {@link java.io.IOException}.</p>
* responsible for reporting {@link java.io.IOException}.
* </p>
*
* <p>Key capabilities are registered independently through
* <p>
* Key capabilities are registered independently through
* {@link SymmetricKeyGenerator}, {@link SymmetricKeyImporter},
* {@link AsymmetricKeyPairGenerator}, {@link PublicKeyImporter}, and
* {@link PrivateKeyImporter}. A provider registers only the operations it
* implements, so capability lookup fails before invocation instead of returning
* an object with unsupported methods.</p>
* an object with unsupported methods.
* </p>
*
* <p>SPI implementations should be stateless or otherwise safe for concurrent
* <p>
* SPI implementations should be stateless or otherwise safe for concurrent
* lookup and invocation. Created cryptographic contexts remain operation-local
* and are not necessarily thread-safe.</p>
* and are not necessarily thread-safe.
* </p>
*
* @since 1.0
*/

View File

@@ -10,9 +10,11 @@ import java.util.Objects;
/**
* Redacted checked failure raised by encrypted keyring operations.
*
* <p>The public message contains only the stable error code. Filesystem paths,
* <p>
* The public message contains only the stable error code. Filesystem paths,
* aliases, key material, ciphertext, and provider-controlled messages are
* deliberately excluded.</p>
* deliberately excluded.
* </p>
*/
public final class KeyringException extends IOException {
private static final long serialVersionUID = 1L;
@@ -21,17 +23,9 @@ public final class KeyringException extends IOException {
* Stable keyring failure categories.
*/
public enum Code {
KEYRING_ALREADY_OPEN,
KEYRING_FILESYSTEM_UNSUPPORTED,
KEYRING_FORMAT_INVALID,
KEYRING_LIMIT_EXCEEDED,
KEYRING_UNLOCK_FAILED,
KEYRING_IO_FAILED,
KEYRING_DURABILITY_UNCONFIRMED,
KEYRING_CLOSED,
KEYRING_NON_EXPORTABLE_KEY,
KEYRING_IMPORT_MAPPING_INVALID,
KEYRING_IMPORT_METADATA_INVALID,
KEYRING_ALREADY_OPEN, KEYRING_FILESYSTEM_UNSUPPORTED, KEYRING_FORMAT_INVALID, KEYRING_LIMIT_EXCEEDED,
KEYRING_UNLOCK_FAILED, KEYRING_IO_FAILED, KEYRING_DURABILITY_UNCONFIRMED, KEYRING_CLOSED,
KEYRING_NON_EXPORTABLE_KEY, KEYRING_IMPORT_MAPPING_INVALID, KEYRING_IMPORT_METADATA_INVALID,
KEYRING_KEY_NOT_CANONICALIZABLE
}

View File

@@ -24,8 +24,7 @@ interface KeyringFileOperations {
/** Atomic persistence destination. */
enum Target {
MAIN_IMAGE,
NONCE_RESERVATION
MAIN_IMAGE, NONCE_RESERVATION
}
/** Creates one owner-only temporary file beside its destination. */
@@ -57,10 +56,8 @@ final class NioKeyringFileOperations implements KeyringFileOperations {
}
@Override
public void writeTemporary(Target target, Path temporary, byte[] image)
throws IOException {
try (FileChannel channel = FileChannel.open(temporary,
StandardOpenOption.WRITE, LinkOption.NOFOLLOW_LINKS)) {
public void writeTemporary(Target target, Path temporary, byte[] image) throws IOException {
try (FileChannel channel = FileChannel.open(temporary, StandardOpenOption.WRITE, LinkOption.NOFOLLOW_LINKS)) {
ByteBuffer buffer = ByteBuffer.wrap(image);
while (buffer.hasRemaining()) {
channel.write(buffer);
@@ -70,17 +67,14 @@ final class NioKeyringFileOperations implements KeyringFileOperations {
@Override
public void forceTemporary(Target target, Path temporary) throws IOException {
try (FileChannel channel = FileChannel.open(temporary,
StandardOpenOption.WRITE, LinkOption.NOFOLLOW_LINKS)) {
try (FileChannel channel = FileChannel.open(temporary, StandardOpenOption.WRITE, LinkOption.NOFOLLOW_LINKS)) {
channel.force(true);
}
}
@Override
public void atomicReplace(Target target, Path temporary, Path destination)
throws IOException {
Files.move(temporary, destination, StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
public void atomicReplace(Target target, Path temporary, Path destination) throws IOException {
Files.move(temporary, destination, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
}
@Override

View File

@@ -71,18 +71,18 @@ import zeroecho.core.spi.SymmetricKeyImporter;
* Closed trusted mapping from persistent key identities to canonical registered
* import operations.
*
* <p>Provider identity is deliberately absent. Standard encoded key material
* is reconstructed by the current runtime's canonical ZeroEcho importer. The
* original JCA provider is neither persisted nor reproduced.</p>
* <p>
* Provider identity is deliberately absent. Standard encoded key material is
* reconstructed by the current runtime's canonical ZeroEcho importer. The
* original JCA provider is neither persisted nor reproduced.
* </p>
*/
final class KeyringImportRegistry {
private static final String ALGORITHM_AES = "AES";
private static final String ALGORITHM_HMAC = "HMAC";
private static final String ALGORITHM_CHACHA20 = "CHACHA20";
private static final String ALGORITHM_CHACHA20_POLY1305 = "CHACHA20-POLY1305";
private static final Map<Class<? extends AlgorithmKeySpec>,
Function<byte[], ? extends AlgorithmKeySpec>> SPEC_FACTORIES =
createSpecFactories();
private static final Map<Class<? extends AlgorithmKeySpec>, Function<byte[], ? extends AlgorithmKeySpec>> SPEC_FACTORIES = createSpecFactories();
private static final Map<Tuple, PersistentMapping> MAPPINGS = createMappings();
private KeyringImportRegistry() {
@@ -93,10 +93,7 @@ final class KeyringImportRegistry {
*/
/* default */
enum HmacVariant {
NONE(0, null),
SHA256(1, "HmacSHA256"),
SHA384(2, "HmacSHA384"),
SHA512(3, "HmacSHA512");
NONE(0, null), SHA256(1, "HmacSHA256"), SHA384(2, "HmacSHA384"), SHA512(3, "HmacSHA512");
private final int code;
private final String jcaName;
@@ -123,8 +120,7 @@ final class KeyringImportRegistry {
throw new KeyringException(KeyringException.Code.KEYRING_IMPORT_METADATA_INVALID);
}
/* default */ static HmacVariant forStoredKey(String algorithmId, Key key)
throws KeyringException {
/* default */ static HmacVariant forStoredKey(String algorithmId, Key key) throws KeyringException {
if (!ALGORITHM_HMAC.equals(algorithmId)) {
return NONE;
}
@@ -142,21 +138,19 @@ final class KeyringImportRegistry {
* Immutable description used by the finite importer-matrix test.
*
* @param algorithmId canonical ZeroEcho algorithm identifier
* @param kind key kind
* @param encoding standard encoding
* @param kind key kind
* @param encoding standard encoding
* @param hmacVariant closed HMAC variant, or {@link HmacVariant#NONE}
* @param specType exact registered importer specification type
* @param specType exact registered importer specification type
*/
/* default */
record PersistentMapping(String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant,
Class<? extends AlgorithmKeySpec> specType) {
record PersistentMapping(String algorithmId, KeyringStore.Kind kind, KeyringStore.Encoding encoding,
HmacVariant hmacVariant, Class<? extends AlgorithmKeySpec> specType) {
}
@SuppressWarnings("PMD.AvoidCatchingGenericException")
/* default */ static Key importKey(String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant, byte[] encoded)
throws GeneralSecurityException, KeyringException {
/* default */ static Key importKey(String algorithmId, KeyringStore.Kind kind, KeyringStore.Encoding encoding,
HmacVariant hmacVariant, byte[] encoded) throws GeneralSecurityException, KeyringException {
PersistentMapping mapping = requireMapping(algorithmId, kind, encoding, hmacVariant);
CryptoAlgorithm algorithm = requireAlgorithm(mapping.algorithmId);
AlgorithmKeySpec spec = createSpec(mapping, encoded);
@@ -172,8 +166,7 @@ final class KeyringImportRegistry {
}
/* default */ static void validateMapping(String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant)
throws KeyringException {
KeyringStore.Encoding encoding, HmacVariant hmacVariant) throws KeyringException {
PersistentMapping mapping = requireMapping(algorithmId, kind, encoding, hmacVariant);
CryptoAlgorithm algorithm = requireAlgorithm(mapping.algorithmId);
List<KeyOperationInfo> operations = matchingOperations(algorithm, kind);
@@ -184,27 +177,23 @@ final class KeyringImportRegistry {
@SuppressWarnings({ "PMD.PreserveStackTrace", "PMD.AvoidCatchingGenericException" })
/* default */ static void validateCanonical(String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant, Key source,
byte[] encoded)
KeyringStore.Encoding encoding, HmacVariant hmacVariant, Key source, byte[] encoded)
throws KeyringException {
Key imported = null;
byte[] canonical = null;
try {
if (!matchesSourceAlgorithm(source, algorithmId, hmacVariant)) {
throw new KeyringException(
KeyringException.Code.KEYRING_KEY_NOT_CANONICALIZABLE);
throw new KeyringException(KeyringException.Code.KEYRING_KEY_NOT_CANONICALIZABLE);
}
imported = importKey(algorithmId, kind, encoding, hmacVariant, encoded);
canonical = imported.getEncoded();
if (canonical == null || !matchesFormat(imported.getFormat(), encoding)
|| !MessageDigest.isEqual(encoded, canonical)
|| !matchesAlgorithm(imported, algorithmId, hmacVariant)) {
throw new KeyringException(
KeyringException.Code.KEYRING_KEY_NOT_CANONICALIZABLE);
throw new KeyringException(KeyringException.Code.KEYRING_KEY_NOT_CANONICALIZABLE);
}
} catch (GeneralSecurityException | RuntimeException exception) {
throw new KeyringException(
KeyringException.Code.KEYRING_KEY_NOT_CANONICALIZABLE);
throw new KeyringException(KeyringException.Code.KEYRING_KEY_NOT_CANONICALIZABLE);
} finally {
if (canonical != null) {
Arrays.fill(canonical, (byte) 0);
@@ -217,11 +206,9 @@ final class KeyringImportRegistry {
return List.copyOf(MAPPINGS.values());
}
private static PersistentMapping requireMapping(String algorithmId,
KeyringStore.Kind kind, KeyringStore.Encoding encoding,
HmacVariant hmacVariant) throws KeyringException {
PersistentMapping mapping = MAPPINGS.get(
new Tuple(algorithmId, kind, encoding, hmacVariant));
private static PersistentMapping requireMapping(String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant) throws KeyringException {
PersistentMapping mapping = MAPPINGS.get(new Tuple(algorithmId, kind, encoding, hmacVariant));
if (mapping == null) {
throw new KeyringException(KeyringException.Code.KEYRING_IMPORT_MAPPING_INVALID);
}
@@ -229,8 +216,7 @@ final class KeyringImportRegistry {
}
@SuppressWarnings("PMD.PreserveStackTrace")
private static CryptoAlgorithm requireAlgorithm(String algorithmId)
throws KeyringException {
private static CryptoAlgorithm requireAlgorithm(String algorithmId) throws KeyringException {
try {
return CryptoAlgorithms.require(algorithmId);
} catch (IllegalArgumentException exception) {
@@ -238,11 +224,8 @@ final class KeyringImportRegistry {
}
}
private static List<KeyOperationInfo> matchingOperations(CryptoAlgorithm algorithm,
KeyringStore.Kind kind) {
return algorithm.keyOperations().stream()
.filter(info -> info.operation() == operation(kind))
.toList();
private static List<KeyOperationInfo> matchingOperations(CryptoAlgorithm algorithm, KeyringStore.Kind kind) {
return algorithm.keyOperations().stream().filter(info -> info.operation() == operation(kind)).toList();
}
private static KeyOperation operation(KeyringStore.Kind kind) {
@@ -254,25 +237,22 @@ final class KeyringImportRegistry {
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Key invokeImporter(CryptoAlgorithm algorithm, KeyringStore.Kind kind,
AlgorithmKeySpec spec) throws GeneralSecurityException {
private static Key invokeImporter(CryptoAlgorithm algorithm, KeyringStore.Kind kind, AlgorithmKeySpec spec)
throws GeneralSecurityException {
return switch (kind) {
case PUBLIC_KEY -> ((PublicKeyImporter) algorithm.publicKeyImporter(spec.getClass()))
.importPublic(spec);
case PRIVATE_KEY -> ((PrivateKeyImporter) algorithm.privateKeyImporter(spec.getClass()))
.importPrivate(spec);
case SECRET_KEY -> ((SymmetricKeyImporter) algorithm.symmetricKeyImporter(spec.getClass()))
.importSecret(spec);
case PUBLIC_KEY -> ((PublicKeyImporter) algorithm.publicKeyImporter(spec.getClass())).importPublic(spec);
case PRIVATE_KEY ->
((PrivateKeyImporter) algorithm.privateKeyImporter(spec.getClass())).importPrivate(spec);
case SECRET_KEY ->
((SymmetricKeyImporter) algorithm.symmetricKeyImporter(spec.getClass())).importSecret(spec);
};
}
private static AlgorithmKeySpec createSpec(PersistentMapping mapping, byte[] encoded)
throws KeyringException {
private static AlgorithmKeySpec createSpec(PersistentMapping mapping, byte[] encoded) throws KeyringException {
if (mapping.specType == HmacKeyImportSpec.class) {
return new HmacKeyImportSpec(mapping.hmacVariant.jcaName, encoded);
}
Function<byte[], ? extends AlgorithmKeySpec> factory =
SPEC_FACTORIES.get(mapping.specType);
Function<byte[], ? extends AlgorithmKeySpec> factory = SPEC_FACTORIES.get(mapping.specType);
AlgorithmKeySpec result = factory == null ? null : factory.apply(encoded);
if (result == null) {
throw new KeyringException(KeyringException.Code.KEYRING_IMPORT_MAPPING_INVALID);
@@ -288,31 +268,27 @@ final class KeyringImportRegistry {
};
}
private static boolean matchesAlgorithm(Key imported, String algorithmId,
HmacVariant hmacVariant) {
private static boolean matchesAlgorithm(Key imported, String algorithmId, HmacVariant hmacVariant) {
if (ALGORITHM_HMAC.equals(algorithmId)) {
return hmacVariant.jcaName.equals(imported.getAlgorithm());
}
if (ALGORITHM_AES.equals(algorithmId)) {
return ALGORITHM_AES.equals(imported.getAlgorithm());
}
if (ALGORITHM_CHACHA20.equals(algorithmId)
|| ALGORITHM_CHACHA20_POLY1305.equals(algorithmId)) {
if (ALGORITHM_CHACHA20.equals(algorithmId) || ALGORITHM_CHACHA20_POLY1305.equals(algorithmId)) {
return "ChaCha20".equals(imported.getAlgorithm());
}
return true;
}
private static boolean matchesSourceAlgorithm(Key source, String algorithmId,
HmacVariant hmacVariant) {
private static boolean matchesSourceAlgorithm(Key source, String algorithmId, HmacVariant hmacVariant) {
if (ALGORITHM_HMAC.equals(algorithmId)) {
return hmacVariant.jcaName.equals(source.getAlgorithm());
}
if (ALGORITHM_AES.equals(algorithmId)) {
return ALGORITHM_AES.equals(source.getAlgorithm());
}
if (ALGORITHM_CHACHA20.equals(algorithmId)
|| ALGORITHM_CHACHA20_POLY1305.equals(algorithmId)) {
if (ALGORITHM_CHACHA20.equals(algorithmId) || ALGORITHM_CHACHA20_POLY1305.equals(algorithmId)) {
return "ChaCha20".equals(source.getAlgorithm());
}
return true;
@@ -329,10 +305,8 @@ final class KeyringImportRegistry {
}
}
private static Map<Class<? extends AlgorithmKeySpec>,
Function<byte[], ? extends AlgorithmKeySpec>> createSpecFactories() {
return Map.ofEntries(
Map.entry(AesKeyImportSpec.class, AesKeyImportSpec::fromRaw),
private static Map<Class<? extends AlgorithmKeySpec>, Function<byte[], ? extends AlgorithmKeySpec>> createSpecFactories() {
return Map.ofEntries(Map.entry(AesKeyImportSpec.class, AesKeyImportSpec::fromRaw),
Map.entry(ChaChaKeyImportSpec.class, ChaChaKeyImportSpec::fromRaw),
Map.entry(BikePublicKeySpec.class, BikePublicKeySpec::new),
Map.entry(BikePrivateKeySpec.class, BikePrivateKeySpec::new),
@@ -380,73 +354,54 @@ final class KeyringImportRegistry {
addAsymmetric(mappings, "CMCE", CmcePublicKeySpec.class, CmcePrivateKeySpec.class);
addAsymmetric(mappings, "DH", DhPublicKeySpec.class, DhPrivateKeySpec.class);
addAsymmetric(mappings, "ECDSA", EcdsaPublicKeySpec.class, EcdsaPrivateKeySpec.class);
addAsymmetric(mappings, "Ed25519", Ed25519PublicKeySpec.class,
Ed25519PrivateKeySpec.class);
addAsymmetric(mappings, "Ed448", Ed448PublicKeySpec.class,
Ed448PrivateKeySpec.class);
addAsymmetric(mappings, "ElGamal", ElgamalPublicKeySpec.class,
ElgamalPrivateKeySpec.class);
addAsymmetric(mappings, "Frodo", FrodoPublicKeySpec.class,
FrodoPrivateKeySpec.class);
addAsymmetric(mappings, "Ed25519", Ed25519PublicKeySpec.class, Ed25519PrivateKeySpec.class);
addAsymmetric(mappings, "Ed448", Ed448PublicKeySpec.class, Ed448PrivateKeySpec.class);
addAsymmetric(mappings, "ElGamal", ElgamalPublicKeySpec.class, ElgamalPrivateKeySpec.class);
addAsymmetric(mappings, "Frodo", FrodoPublicKeySpec.class, FrodoPrivateKeySpec.class);
addAsymmetric(mappings, "HQC", HqcPublicKeySpec.class, HqcPrivateKeySpec.class);
addAsymmetric(mappings, "ML-KEM", KyberPublicKeySpec.class,
KyberPrivateKeySpec.class);
addAsymmetric(mappings, "ML-DSA", MldsaPublicKeySpec.class,
MldsaPrivateKeySpec.class);
addAsymmetric(mappings, "ML-KEM", KyberPublicKeySpec.class, KyberPrivateKeySpec.class);
addAsymmetric(mappings, "ML-DSA", MldsaPublicKeySpec.class, MldsaPrivateKeySpec.class);
addAsymmetric(mappings, "NTRU", NtruPublicKeySpec.class, NtruPrivateKeySpec.class);
addAsymmetric(mappings, "NTRULPRime", NtrulPrimePublicKeySpec.class,
NtrulPrimePrivateKeySpec.class);
addAsymmetric(mappings, "SNTRUPrime", SntruPrimePublicKeySpec.class,
SntruPrimePrivateKeySpec.class);
addAsymmetric(mappings, "NTRULPRime", NtrulPrimePublicKeySpec.class, NtrulPrimePrivateKeySpec.class);
addAsymmetric(mappings, "SNTRUPrime", SntruPrimePublicKeySpec.class, SntruPrimePrivateKeySpec.class);
addAsymmetric(mappings, "RSA", RsaPublicKeySpec.class, RsaPrivateKeySpec.class);
addAsymmetric(mappings, "SABER", SaberPublicKeySpec.class,
SaberPrivateKeySpec.class);
addAsymmetric(mappings, "SLH-DSA", SlhDsaPublicKeySpec.class,
SlhDsaPrivateKeySpec.class);
addAsymmetric(mappings, "SPHINCS+", SphincsPlusPublicKeySpec.class,
SphincsPlusPrivateKeySpec.class);
addAsymmetric(mappings, "SABER", SaberPublicKeySpec.class, SaberPrivateKeySpec.class);
addAsymmetric(mappings, "SLH-DSA", SlhDsaPublicKeySpec.class, SlhDsaPrivateKeySpec.class);
addAsymmetric(mappings, "SPHINCS+", SphincsPlusPublicKeySpec.class, SphincsPlusPrivateKeySpec.class);
addAsymmetric(mappings, "Xdh", XdhPublicKeySpec.class, XdhPrivateKeySpec.class);
add(mappings, ALGORITHM_AES, KeyringStore.Kind.SECRET_KEY,
KeyringStore.Encoding.RAW,
HmacVariant.NONE, AesKeyImportSpec.class);
add(mappings, ALGORITHM_CHACHA20, KeyringStore.Kind.SECRET_KEY,
KeyringStore.Encoding.RAW,
add(mappings, ALGORITHM_AES, KeyringStore.Kind.SECRET_KEY, KeyringStore.Encoding.RAW, HmacVariant.NONE,
AesKeyImportSpec.class);
add(mappings, ALGORITHM_CHACHA20, KeyringStore.Kind.SECRET_KEY, KeyringStore.Encoding.RAW, HmacVariant.NONE,
ChaChaKeyImportSpec.class);
add(mappings, ALGORITHM_CHACHA20_POLY1305, KeyringStore.Kind.SECRET_KEY, KeyringStore.Encoding.RAW,
HmacVariant.NONE, ChaChaKeyImportSpec.class);
add(mappings, ALGORITHM_CHACHA20_POLY1305, KeyringStore.Kind.SECRET_KEY,
KeyringStore.Encoding.RAW, HmacVariant.NONE, ChaChaKeyImportSpec.class);
add(mappings, ALGORITHM_HMAC, KeyringStore.Kind.SECRET_KEY,
KeyringStore.Encoding.RAW,
HmacVariant.SHA256, HmacKeyImportSpec.class);
add(mappings, ALGORITHM_HMAC, KeyringStore.Kind.SECRET_KEY,
KeyringStore.Encoding.RAW,
HmacVariant.SHA384, HmacKeyImportSpec.class);
add(mappings, ALGORITHM_HMAC, KeyringStore.Kind.SECRET_KEY,
KeyringStore.Encoding.RAW,
HmacVariant.SHA512, HmacKeyImportSpec.class);
add(mappings, ALGORITHM_HMAC, KeyringStore.Kind.SECRET_KEY, KeyringStore.Encoding.RAW, HmacVariant.SHA256,
HmacKeyImportSpec.class);
add(mappings, ALGORITHM_HMAC, KeyringStore.Kind.SECRET_KEY, KeyringStore.Encoding.RAW, HmacVariant.SHA384,
HmacKeyImportSpec.class);
add(mappings, ALGORITHM_HMAC, KeyringStore.Kind.SECRET_KEY, KeyringStore.Encoding.RAW, HmacVariant.SHA512,
HmacKeyImportSpec.class);
return Collections.unmodifiableMap(mappings);
}
private static void addAsymmetric(Map<Tuple, PersistentMapping> mappings,
String algorithmId, Class<? extends AlgorithmKeySpec> publicSpec,
Class<? extends AlgorithmKeySpec> privateSpec) {
add(mappings, algorithmId, KeyringStore.Kind.PUBLIC_KEY,
KeyringStore.Encoding.X509, HmacVariant.NONE, publicSpec);
add(mappings, algorithmId, KeyringStore.Kind.PRIVATE_KEY,
KeyringStore.Encoding.PKCS8, HmacVariant.NONE, privateSpec);
private static void addAsymmetric(Map<Tuple, PersistentMapping> mappings, String algorithmId,
Class<? extends AlgorithmKeySpec> publicSpec, Class<? extends AlgorithmKeySpec> privateSpec) {
add(mappings, algorithmId, KeyringStore.Kind.PUBLIC_KEY, KeyringStore.Encoding.X509, HmacVariant.NONE,
publicSpec);
add(mappings, algorithmId, KeyringStore.Kind.PRIVATE_KEY, KeyringStore.Encoding.PKCS8, HmacVariant.NONE,
privateSpec);
}
private static void add(Map<Tuple, PersistentMapping> mappings, String algorithmId,
KeyringStore.Kind kind, KeyringStore.Encoding encoding,
HmacVariant hmacVariant, Class<? extends AlgorithmKeySpec> specType) {
private static void add(Map<Tuple, PersistentMapping> mappings, String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant, Class<? extends AlgorithmKeySpec> specType) {
Tuple tuple = new Tuple(algorithmId, kind, encoding, hmacVariant);
PersistentMapping mapping = new PersistentMapping(algorithmId, kind,
encoding, hmacVariant, specType);
PersistentMapping mapping = new PersistentMapping(algorithmId, kind, encoding, hmacVariant, specType);
if (mappings.put(tuple, mapping) != null) {
throw new IllegalStateException("Duplicate persistent key importer tuple");
}
}
private record Tuple(String algorithmId, KeyringStore.Kind kind,
KeyringStore.Encoding encoding, HmacVariant hmacVariant) {
private record Tuple(String algorithmId, KeyringStore.Kind kind, KeyringStore.Encoding encoding,
HmacVariant hmacVariant) {
}
}

View File

@@ -19,8 +19,7 @@ final class KeyringNonceReservationKdf {
private static final int STORE_ID_BYTES = 16;
private static final int OUTPUT_BYTES = 32;
private static final String HMAC_SHA256 = "HmacSHA256";
private static final String DOMAIN_LABEL =
"zeroecho:keyring:nonce-reservation-mac:v1";
private static final String DOMAIN_LABEL = "zeroecho:keyring:nonce-reservation-mac:v1";
private KeyringNonceReservationKdf() {
}
@@ -29,14 +28,13 @@ final class KeyringNonceReservationKdf {
* Derives the store-specific nonce-reservation MAC key.
*
* @param masterKey borrowed 256-bit store master key
* @param storeId borrowed canonical 128-bit binary store UUID
* @param storeId borrowed canonical 128-bit binary store UUID
* @return newly owned 256-bit derived key
* @throws GeneralSecurityException if HMAC-SHA-256 is unavailable
*/
/* default */ static byte[] derive(byte[] masterKey, byte[] storeId)
throws GeneralSecurityException {
if (masterKey == null || masterKey.length != MASTER_KEY_BYTES
|| storeId == null || storeId.length != STORE_ID_BYTES) {
/* default */ static byte[] derive(byte[] masterKey, byte[] storeId) throws GeneralSecurityException {
if (masterKey == null || masterKey.length != MASTER_KEY_BYTES || storeId == null
|| storeId.length != STORE_ID_BYTES) {
throw new IllegalArgumentException("Invalid keyring derivation input");
}
byte[] salt = storeId.clone();
@@ -61,8 +59,7 @@ final class KeyringNonceReservationKdf {
}
}
private static byte[] hmac(byte[] key, byte[] input)
throws GeneralSecurityException {
private static byte[] hmac(byte[] key, byte[] input) throws GeneralSecurityException {
Mac mac = Mac.getInstance(HMAC_SHA256);
mac.init(new SecretKeySpec(key, HMAC_SHA256));
return mac.doFinal(input);

View File

@@ -14,14 +14,18 @@ import javax.security.auth.Destroyable;
/**
* Destroyable owner of a keyring password.
*
* <p>The constructor and {@link #copy()} use defensive copies. Callers retain
* <p>
* The constructor and {@link #copy()} use defensive copies. Callers retain
* ownership of the array supplied to the constructor and must clear it. The
* returned copy belongs to the receiver and must be cleared immediately after
* key derivation. This object never creates an immutable password
* {@link String}.</p>
* {@link String}.
* </p>
*
* <p>Instances are thread-safe. Destruction is idempotent and makes subsequent
* access fail deterministically.</p>
* <p>
* Instances are thread-safe. Destruction is idempotent and makes subsequent
* access fail deterministically.
* </p>
*/
public final class KeyringPassword implements Destroyable, AutoCloseable {
private final ReentrantLock lifecycleLock = new ReentrantLock();
@@ -32,7 +36,7 @@ public final class KeyringPassword implements Destroyable, AutoCloseable {
* Creates a password owner.
*
* @param password password characters, which are defensively copied
* @throws NullPointerException if {@code password} is {@code null}
* @throws NullPointerException if {@code password} is {@code null}
* @throws IllegalArgumentException if {@code password} is empty
*/
@SuppressWarnings("PMD.UseVarargs")

View File

@@ -8,7 +8,8 @@ package zeroecho.core.storage;
* Operational limits applied while opening an encrypted software keyring.
*
* @param operationalIterationMaximum maximum accepted PBKDF2 iteration count;
* it may restrict but never exceed the absolute decoded maximum
* it may restrict but never exceed the
* absolute decoded maximum
*/
public record KeyringProtection(int operationalIterationMaximum) {
/** Iterations used when a new keyring is created. */
@@ -21,8 +22,8 @@ public record KeyringProtection(int operationalIterationMaximum) {
/**
* Validates the operational limit.
*
* @throws IllegalArgumentException if the limit is below the creation
* setting or above the absolute operational maximum
* @throws IllegalArgumentException if the limit is below the creation setting
* or above the absolute operational maximum
*/
public KeyringProtection {
if (operationalIterationMaximum < CREATION_ITERATIONS

View File

@@ -7,8 +7,10 @@ package zeroecho.core.storage;
/**
* Fills keyring randomness buffers.
*
* <p>This package-private seam supports deterministic format tests; production
* creation uses the authoritative shared secure random source.</p>
* <p>
* This package-private seam supports deterministic format tests; production
* creation uses the authoritative shared secure random source.
* </p>
*/
@FunctionalInterface
interface KeyringRandomBytes {

File diff suppressed because it is too large Load Diff

View File

@@ -53,15 +53,15 @@
* </p>
*
* <p>
* Unlock passwords are destroyable, transfer ownership to the receiver, and
* are destroyed immediately after the master key is unwrapped. The unlocked
* store retains the master key, its domain-separated nonce-reservation MAC
* key, and encrypted entry records; closing the store clears this material.
* The store requires a POSIX filesystem on which owner-only permissions can be
* verified. A directory-force failure after atomic replacement makes the open
* instance unusable until close and authenticated reopen resolves which
* complete image is current. Non-exportable keys must remain behind an
* external provider reference.
* Unlock passwords are destroyable, transfer ownership to the receiver, and are
* destroyed immediately after the master key is unwrapped. The unlocked store
* retains the master key, its domain-separated nonce-reservation MAC key, and
* encrypted entry records; closing the store clears this material. The store
* requires a POSIX filesystem on which owner-only permissions can be verified.
* A directory-force failure after atomic replacement makes the open instance
* unusable until close and authenticated reopen resolves which complete image
* is current. Non-exportable keys must remain behind an external provider
* reference.
* </p>
*
* <p>

View File

@@ -124,8 +124,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
public static TagEngineBuilder<byte[]> digest(final ZeroEchoSession session, final DigestSpec spec) {
Objects.requireNonNull(session, "session");
final DigestSpec s = spec == null ? DigestSpec.sha256() : spec;
return new TagEngineBuilder<>(
() -> session.createContext("DIGEST", KeyUsage.DIGEST, NullKey.INSTANCE, s));
return new TagEngineBuilder<>(() -> session.createContext("DIGEST", KeyUsage.DIGEST, NullKey.INSTANCE, s));
}
/**
@@ -193,8 +192,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
* @return a builder that produces Ed25519 signature engines in SIGN mode
* @throws NullPointerException if {@code privateKey} is {@code null}
*/
public static TagEngineBuilder<Signature> ed25519Sign(final ZeroEchoSession session,
final PrivateKey privateKey) {
public static TagEngineBuilder<Signature> ed25519Sign(final ZeroEchoSession session, final PrivateKey privateKey) {
Objects.requireNonNull(privateKey, PRIVATE_KEY);
return signature(session, "Ed25519", privateKey, VoidSpec.INSTANCE);
}
@@ -206,8 +204,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
* @return a builder that produces Ed25519 signature engines in VERIFY mode
* @throws NullPointerException if {@code publicKey} is {@code null}
*/
public static TagEngineBuilder<Signature> ed25519Verify(final ZeroEchoSession session,
final PublicKey publicKey) {
public static TagEngineBuilder<Signature> ed25519Verify(final ZeroEchoSession session, final PublicKey publicKey) {
Objects.requireNonNull(publicKey, PUBLIC_KEY);
return signature(session, "Ed25519", publicKey, VoidSpec.INSTANCE);
}
@@ -229,8 +226,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
public static TagEngineBuilder<Signature> rsaSign(final ZeroEchoSession session, final PrivateKey privateKey,
final RsaSigSpec spec) {
Objects.requireNonNull(privateKey, PRIVATE_KEY);
return signature(session, "RSA", privateKey,
spec == null ? RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32) : spec);
return signature(session, "RSA", privateKey, spec == null ? RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32) : spec);
}
/**
@@ -250,8 +246,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
public static TagEngineBuilder<Signature> rsaVerify(final ZeroEchoSession session, final PublicKey publicKey,
final RsaSigSpec spec) {
Objects.requireNonNull(publicKey, PUBLIC_KEY);
return signature(session, "RSA", publicKey,
spec == null ? RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32) : spec);
return signature(session, "RSA", publicKey, spec == null ? RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32) : spec);
}
/**
@@ -369,8 +364,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
* @return a builder that produces SLH-DSA signature engines in SIGN mode
* @throws NullPointerException if {@code privateKey} is {@code null}
*/
public static TagEngineBuilder<Signature> slhDsaSign(final ZeroEchoSession session,
final PrivateKey privateKey) {
public static TagEngineBuilder<Signature> slhDsaSign(final ZeroEchoSession session, final PrivateKey privateKey) {
Objects.requireNonNull(privateKey, PRIVATE_KEY);
return signature(session, "SLH-DSA", privateKey, VoidSpec.INSTANCE);
}
@@ -388,8 +382,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
* @return a builder that produces SLH-DSA signature engines in VERIFY mode
* @throws NullPointerException if {@code publicKey} is {@code null}
*/
public static TagEngineBuilder<Signature> slhDsaVerify(final ZeroEchoSession session,
final PublicKey publicKey) {
public static TagEngineBuilder<Signature> slhDsaVerify(final ZeroEchoSession session, final PublicKey publicKey) {
Objects.requireNonNull(publicKey, PUBLIC_KEY);
return signature(session, "SLH-DSA", publicKey, VoidSpec.INSTANCE);
}
@@ -408,8 +401,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
* @return a builder that produces ML-DSA signature engines in SIGN mode
* @throws NullPointerException if {@code privateKey} is {@code null}
*/
public static TagEngineBuilder<Signature> mldsaSign(final ZeroEchoSession session,
final PrivateKey privateKey) {
public static TagEngineBuilder<Signature> mldsaSign(final ZeroEchoSession session, final PrivateKey privateKey) {
Objects.requireNonNull(privateKey, PRIVATE_KEY);
return signature(session, "ML-DSA", privateKey, VoidSpec.INSTANCE);
}
@@ -428,8 +420,7 @@ public final class TagEngineBuilder<T> implements Supplier<TagEngine<T>> {
* @return a builder that produces ML-DSA signature engines in VERIFY mode
* @throws NullPointerException if {@code publicKey} is {@code null}
*/
public static TagEngineBuilder<Signature> mldsaVerify(final ZeroEchoSession session,
final PublicKey publicKey) {
public static TagEngineBuilder<Signature> mldsaVerify(final ZeroEchoSession session, final PublicKey publicKey) {
Objects.requireNonNull(publicKey, PUBLIC_KEY);
return signature(session, "ML-DSA", publicKey, VoidSpec.INSTANCE);
}

View File

@@ -25,9 +25,11 @@ import zeroecho.core.spi.SymmetricKeyImporter;
/**
* Session-bound entry point for exact key-material operations.
*
* <p>Capability lookup fails before an operation object is returned. Returned
* <p>
* Capability lookup fails before an operation object is returned. Returned
* objects guarantee the requested operation and report successful execution to
* the owning session's audit listener on a best-effort basis.</p>
* the owning session's audit listener on a best-effort basis.
* </p>
*
* @since 1.0
*/
@@ -69,13 +71,12 @@ public final class KeyBuilders {
* Resolves an exact symmetric generator.
*
* @param algorithmId canonical algorithm identifier
* @param specType exact specification class
* @param <S> specification type
* @param specType exact specification class
* @param <S> specification type
* @return guaranteed generator
* @throws IllegalArgumentException if the capability is absent
*/
public <S extends AlgorithmKeySpec> SymmetricKeyGenerator<S> generator(String algorithmId,
Class<S> specType) {
public <S extends AlgorithmKeySpec> SymmetricKeyGenerator<S> generator(String algorithmId, Class<S> specType) {
CryptoAlgorithm algorithm = session.require(algorithmId);
SymmetricKeyGenerator<S> delegate = algorithm.symmetricKeyGenerator(specType);
return spec -> {
@@ -89,13 +90,12 @@ public final class KeyBuilders {
* Resolves an exact symmetric importer.
*
* @param algorithmId canonical algorithm identifier
* @param specType exact specification class
* @param <S> specification type
* @param specType exact specification class
* @param <S> specification type
* @return guaranteed importer
* @throws IllegalArgumentException if the capability is absent
*/
public <S extends AlgorithmKeySpec> SymmetricKeyImporter<S> importer(String algorithmId,
Class<S> specType) {
public <S extends AlgorithmKeySpec> SymmetricKeyImporter<S> importer(String algorithmId, Class<S> specType) {
CryptoAlgorithm algorithm = session.require(algorithmId);
SymmetricKeyImporter<S> delegate = algorithm.symmetricKeyImporter(specType);
return spec -> {
@@ -109,12 +109,13 @@ public final class KeyBuilders {
* Generates a symmetric key using the exact runtime specification type.
*
* @param algorithmId canonical algorithm identifier
* @param spec generation specification
* @param <S> specification type
* @param spec generation specification
* @param <S> specification type
* @return generated secret key
* @throws java.security.GeneralSecurityException if generation fails
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is {@code null}
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is
* {@code null}
*/
public <S extends AlgorithmKeySpec> SecretKey generate(String algorithmId, S spec)
throws java.security.GeneralSecurityException {
@@ -128,12 +129,13 @@ public final class KeyBuilders {
* Imports a symmetric key using the exact runtime specification type.
*
* @param algorithmId canonical algorithm identifier
* @param spec import specification
* @param <S> specification type
* @param spec import specification
* @param <S> specification type
* @return imported secret key
* @throws java.security.GeneralSecurityException if import fails
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is {@code null}
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is
* {@code null}
*/
public <S extends AlgorithmKeySpec> SecretKey importKey(String algorithmId, S spec)
throws java.security.GeneralSecurityException {
@@ -155,8 +157,8 @@ public final class KeyBuilders {
* Resolves an exact key-pair generator.
*
* @param algorithmId canonical algorithm identifier
* @param specType exact specification class
* @param <S> specification type
* @param specType exact specification class
* @param <S> specification type
* @return guaranteed generator
* @throws IllegalArgumentException if the capability is absent
*/
@@ -175,13 +177,12 @@ public final class KeyBuilders {
* Resolves an exact public-key importer.
*
* @param algorithmId canonical algorithm identifier
* @param specType exact specification class
* @param <S> specification type
* @param specType exact specification class
* @param <S> specification type
* @return guaranteed importer
* @throws IllegalArgumentException if the capability is absent
*/
public <S extends AlgorithmKeySpec> PublicKeyImporter<S> publicImporter(String algorithmId,
Class<S> specType) {
public <S extends AlgorithmKeySpec> PublicKeyImporter<S> publicImporter(String algorithmId, Class<S> specType) {
CryptoAlgorithm algorithm = session.require(algorithmId);
PublicKeyImporter<S> delegate = algorithm.publicKeyImporter(specType);
return spec -> {
@@ -195,8 +196,8 @@ public final class KeyBuilders {
* Resolves an exact private-key importer.
*
* @param algorithmId canonical algorithm identifier
* @param specType exact specification class
* @param <S> specification type
* @param specType exact specification class
* @param <S> specification type
* @return guaranteed importer
* @throws IllegalArgumentException if the capability is absent
*/
@@ -215,12 +216,13 @@ public final class KeyBuilders {
* Generates a key pair using the exact runtime specification type.
*
* @param algorithmId canonical algorithm identifier
* @param spec generation specification
* @param <S> specification type
* @param spec generation specification
* @param <S> specification type
* @return generated key pair
* @throws java.security.GeneralSecurityException if generation fails
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is {@code null}
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is
* {@code null}
*/
public <S extends AlgorithmKeySpec> KeyPair generateKeyPair(String algorithmId, S spec)
throws java.security.GeneralSecurityException {
@@ -234,12 +236,13 @@ public final class KeyBuilders {
* Imports a public key using the exact runtime specification type.
*
* @param algorithmId canonical algorithm identifier
* @param spec public-key import specification
* @param <S> specification type
* @param spec public-key import specification
* @param <S> specification type
* @return imported public key
* @throws java.security.GeneralSecurityException if import fails
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is {@code null}
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is
* {@code null}
*/
public <S extends AlgorithmKeySpec> PublicKey importPublic(String algorithmId, S spec)
throws java.security.GeneralSecurityException {
@@ -253,12 +256,13 @@ public final class KeyBuilders {
* Imports a private key using the exact runtime specification type.
*
* @param algorithmId canonical algorithm identifier
* @param spec private-key import specification
* @param <S> specification type
* @param spec private-key import specification
* @param <S> specification type
* @return imported private key
* @throws java.security.GeneralSecurityException if import fails
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is {@code null}
* @throws IllegalArgumentException if the capability is absent
* @throws NullPointerException if {@code spec} is
* {@code null}
*/
public <S extends AlgorithmKeySpec> PrivateKey importPrivate(String algorithmId, S spec)
throws java.security.GeneralSecurityException {

View File

@@ -8,10 +8,11 @@
package zeroecho.sdk;
/**
* Explicit PBKDF2 work-factor limits for trusted configuration and decoded data.
* Explicit PBKDF2 work-factor limits for trusted configuration and decoded
* data.
*
* @param operationalMaximum largest iteration count accepted from trusted local
* configuration
* @param operationalMaximum largest iteration count accepted from trusted
* local configuration
* @param absoluteDecodedMaximum hard safety ceiling for untrusted decoded data
* @since 1.0
*/
@@ -41,8 +42,8 @@ public record Pbkdf2Limits(int operationalMaximum, int absoluteDecodedMaximum) {
*/
public void validateTrusted(int iterations) {
if (iterations < MINIMUM || iterations > operationalMaximum) {
throw new IllegalArgumentException("PBKDF2 iterations must be in range " + MINIMUM + ".."
+ operationalMaximum + ": " + iterations);
throw new IllegalArgumentException(
"PBKDF2 iterations must be in range " + MINIMUM + ".." + operationalMaximum + ": " + iterations);
}
}

View File

@@ -108,8 +108,8 @@ public final class ZeroEchoSession {
this(CryptoPolicy.permissive(), AuditListener.noop(), AuditMode.OFF, null);
}
private ZeroEchoSession(CryptoPolicy<ContextSpec, Key> policy, AuditListener auditListener,
AuditMode auditMode, Pbkdf2Limits pbkdf2Limits) {
private ZeroEchoSession(CryptoPolicy<ContextSpec, Key> policy, AuditListener auditListener, AuditMode auditMode,
Pbkdf2Limits pbkdf2Limits) {
this.policy = Objects.requireNonNull(policy, "policy must not be null");
this.auditListener = Objects.requireNonNull(auditListener, "auditListener must not be null");
this.auditSink = AuditListeners.bestEffort(auditListener);
@@ -143,8 +143,7 @@ public final class ZeroEchoSession {
*/
public ZeroEchoSession withAuditListener(AuditListener newAuditListener) {
return new ZeroEchoSession(policy,
Objects.requireNonNull(newAuditListener, "newAuditListener must not be null"), auditMode,
pbkdf2Limits);
Objects.requireNonNull(newAuditListener, "newAuditListener must not be null"), auditMode, pbkdf2Limits);
}
/**
@@ -198,8 +197,8 @@ public final class ZeroEchoSession {
* Returns the audit listener owned by this session.
*
* <p>
* The returned listener is the configured strategy, not mutable session
* state. It is exposed to support manual audit mode.
* The returned listener is the configured strategy, not mutable session state.
* It is exposed to support manual audit mode.
* </p>
*
* @return the non-null audit listener
@@ -254,8 +253,8 @@ public final class ZeroEchoSession {
* @param id canonical algorithm identifier
* @param role intended key usage
* @param key key compatible with the selected algorithm and role
* @param spec optional context specification, or {@code null} for the
* algorithm default
* @param spec optional context specification, or {@code null} for the algorithm
* default
* @param <C> context type
* @param <K> key type
* @param <S> context specification type
@@ -275,8 +274,8 @@ public final class ZeroEchoSession {
return finishContext(algorithm, context, role, spec);
}
private <C extends CryptoContext, S extends ContextSpec> C finishContext(CryptoAlgorithm algorithm,
C context, KeyUsage role, S spec) {
private <C extends CryptoContext, S extends ContextSpec> C finishContext(CryptoAlgorithm algorithm, C context,
KeyUsage role, S spec) {
if (auditMode == AuditMode.OFF) {
notifyContextCreated(algorithm, role, spec);
return context;
@@ -321,14 +320,15 @@ public final class ZeroEchoSession {
* Destroys a key and verifies that it entered the destroyed state.
*
* @param algorithmId algorithm identifier used as audit metadata
* @param provider provider name used as audit metadata
* @param key key to destroy; must not be {@code null}
* @param provider provider name used as audit metadata
* @param key key to destroy; must not be {@code null}
* @return {@code true} only when this call transitions the key to destroyed;
* {@code false} for a non-destroyable or already destroyed key
* @throws NullPointerException if {@code key} is {@code null}
* @throws NullPointerException if {@code key} is {@code null}
* @throws DestroyFailedException if destruction fails or the key does not
* report itself destroyed afterward
* @throws RuntimeException if the key's lifecycle implementation throws one
* @throws RuntimeException if the key's lifecycle implementation throws
* one
*/
public boolean destroyKey(String algorithmId, String provider, Key key) throws DestroyFailedException {
Objects.requireNonNull(key, "key must not be null");
@@ -352,12 +352,10 @@ public final class ZeroEchoSession {
return true;
}
private <S extends ContextSpec> void notifyContextCreated(CryptoAlgorithm algorithm,
KeyUsage role, S spec) {
Map<String, Object> metadata = spec == null ? Map.of()
: Map.of("specType", spec.getClass().getName());
auditSink.onContextCreatedMeta(UUID.randomUUID().toString(), algorithm.id(), algorithm.providerName(),
role, "n/a", metadata);
private <S extends ContextSpec> void notifyContextCreated(CryptoAlgorithm algorithm, KeyUsage role, S spec) {
Map<String, Object> metadata = spec == null ? Map.of() : Map.of("specType", spec.getClass().getName());
auditSink.onContextCreatedMeta(UUID.randomUUID().toString(), algorithm.id(), algorithm.providerName(), role,
"n/a", metadata);
}
/* default */ void notifyKeyPairGenerated(CryptoAlgorithm algorithm, AlgorithmKeySpec spec, KeyPair keyPair) {

View File

@@ -342,10 +342,8 @@ public final class HybridKexBuilder {
if (pqcAlgId == null) {
throw new IllegalStateException("pqc algorithm id must be set");
}
if (classicMode == ClassicMode.CLASSIC_AGREEMENT
&& (classicPrivate == null || classicPeerPublic == null)) {
throw new IllegalStateException(
"classic private key and peer public must be set for CLASSIC_AGREEMENT");
if (classicMode == ClassicMode.CLASSIC_AGREEMENT && (classicPrivate == null || classicPeerPublic == null)) {
throw new IllegalStateException("classic private key and peer public must be set for CLASSIC_AGREEMENT");
}
if (classicMode == ClassicMode.PAIR_MESSAGE && classicKeyPair == null) {
throw new IllegalStateException("classic key pair must be set for PAIR_MESSAGE");

View File

@@ -77,12 +77,13 @@ import zeroecho.sdk.hybrid.signature.HybridSignatureProfile;
* <li>{@link #single(ZeroEchoSession)}: constructs a non-hybrid
* {@code SignatureContext}.</li>
* <li>{@link #hybrid(ZeroEchoSession)}: constructs a hybrid
* {@code SignatureContext} via
* {@link HybridSignatureContexts}.</li>
* {@code SignatureContext} via {@link HybridSignatureContexts}.</li>
* </ul>
*
* <p>Context construction is in-memory. Checked I/O failures arise only when a
* built stream is attached or processed.</p>
* <p>
* Context construction is in-memory. Checked I/O failures arise only when a
* built stream is attached or processed.
* </p>
*
* @since 1.0
*/
@@ -253,8 +254,8 @@ public final class SignatureTrailerDataContentBuilder implements DataContentBuil
Objects.requireNonNull(algorithmId, "algorithmId");
Objects.requireNonNull(privateKey, "privateKey");
Supplier<TagEngine<Signature>> factory = () -> session.createContext(algorithmId, KeyUsage.SIGN,
privateKey, spec);
Supplier<TagEngine<Signature>> factory = () -> session.createContext(algorithmId, KeyUsage.SIGN, privateKey,
spec);
return core(factory);
}
@@ -293,8 +294,8 @@ public final class SignatureTrailerDataContentBuilder implements DataContentBuil
Objects.requireNonNull(algorithmId, "algorithmId");
Objects.requireNonNull(publicKey, "publicKey");
Supplier<TagEngine<Signature>> factory = () -> session.createContext(algorithmId,
KeyUsage.VERIFY, publicKey, spec);
Supplier<TagEngine<Signature>> factory = () -> session.createContext(algorithmId, KeyUsage.VERIFY,
publicKey, spec);
return core(factory);
}

View File

@@ -567,8 +567,7 @@ public final class ChaChaDataContentBuilder implements DataContentBuilder<DataCo
* <p>
* The actual cipher work is delegated to an
* {@link zeroecho.core.context.EncryptionContext} created through
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}.
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}.
* If the created context implements {@code ContextAware}, the configured
* context is injected before the stream is attached.
* </p>
@@ -638,8 +637,7 @@ public final class ChaChaDataContentBuilder implements DataContentBuilder<DataCo
* <p>
* The actual cipher work is delegated to an
* {@link zeroecho.core.context.EncryptionContext} created through
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}.
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)}.
* If the created context implements {@code ContextAware}, the configured
* context is injected before the stream is attached.
* </p>

View File

@@ -113,6 +113,7 @@ import zeroecho.sdk.content.api.PlainContent;
*/
public final class DigestDataContentBuilder implements DataContentBuilder<PlainContent> {
private final ZeroEchoSession session;
/**
* OutputMode selects how the digest-computing pipeline presents its result to
* callers.

View File

@@ -361,8 +361,7 @@ public final class ElgamalEncDataContentBuilder implements DataContentBuilder<Da
* {@link ElgamalEncSpec}. When {@link #getStream()} is called, it creates an
* {@link zeroecho.core.context.EncryptionContext} for the "ElGamal" algorithm
* in {@link zeroecho.core.KeyUsage#ENCRYPT} role via
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* attaches the upstream stream, and returns a pull-based stream that encrypts
* on the fly.
* </p>
@@ -426,8 +425,7 @@ public final class ElgamalEncDataContentBuilder implements DataContentBuilder<Da
* {@link ElgamalEncSpec}. When {@link #getStream()} is called, it creates an
* {@link zeroecho.core.context.EncryptionContext} for the "ElGamal" algorithm
* in {@link zeroecho.core.KeyUsage#DECRYPT} role via
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* attaches the upstream stream, and returns a pull-based stream that decrypts
* on the fly.
* </p>

View File

@@ -91,6 +91,7 @@ import zeroecho.sdk.content.api.PlainContent;
public final class HmacDataContentBuilder implements DataContentBuilder<PlainContent> {
private static final String ALGORITHM_ID = "HMAC";
private final ZeroEchoSession session;
/**
* Mode selects whether the pipeline computes an HMAC tag or verifies one.
*
@@ -622,8 +623,7 @@ public final class HmacDataContentBuilder implements DataContentBuilder<PlainCon
final String mac = spec.macName(); // e.g., "HmacSHA256"
try {
if (genKeyBits != null) {
return session.keyBuilders().symmetric().generate(ALGORITHM_ID,
new HmacKeyGenSpec(mac, genKeyBits));
return session.keyBuilders().symmetric().generate(ALGORITHM_ID, new HmacKeyGenSpec(mac, genKeyBits));
}
if (importRaw != null || importHex != null || importBase64 != null) {
HmacKeyImportSpec ispec;

View File

@@ -345,8 +345,7 @@ public final class RsaEncDataContentBuilder implements DataContentBuilder<DataCo
* When {@link #getStream()} is invoked, this class creates an
* {@link zeroecho.core.context.EncryptionContext} for the "RSA" algorithm in
* {@link zeroecho.core.KeyUsage#ENCRYPT} role via
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* attaches the upstream stream, and returns a pull-based stream that encrypts
* on-the-fly using the configured {@link RsaEncSpec}.
* </p>
@@ -407,8 +406,7 @@ public final class RsaEncDataContentBuilder implements DataContentBuilder<DataCo
* When {@link #getStream()} is invoked, this class creates an
* {@link zeroecho.core.context.EncryptionContext} for the "RSA" algorithm in
* {@link zeroecho.core.KeyUsage#DECRYPT} role via
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String,
* zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* {@link zeroecho.sdk.ZeroEchoSession#createContext(String, zeroecho.core.KeyUsage, java.security.Key, zeroecho.core.spec.ContextSpec)},
* attaches the upstream stream, and returns a pull-based stream that decrypts
* on-the-fly using the configured {@link RsaEncSpec}.
* </p>

View File

@@ -109,6 +109,7 @@ import zeroecho.sdk.content.api.PlainContent;
public final class RsaSigDataContentBuilder implements DataContentBuilder<PlainContent> {
private static final String ALGORITHM_ID = "RSA";
private final ZeroEchoSession session;
/**
* Mode selects whether the builder signs or verifies.
*/

View File

@@ -70,7 +70,8 @@
* {@link zeroecho.sdk.builders.alg.ElgamalEncDataContentBuilder}.</li>
* <li>RSA signatures:
* {@link zeroecho.sdk.builders.alg.RsaSigDataContentBuilder}; generic signature
* trailers use {@link zeroecho.sdk.builders.SignatureTrailerDataContentBuilder}.</li>
* trailers use
* {@link zeroecho.sdk.builders.SignatureTrailerDataContentBuilder}.</li>
* <li>MAC and digest: {@link zeroecho.sdk.builders.alg.HmacDataContentBuilder},
* {@link zeroecho.sdk.builders.alg.DigestDataContentBuilder}.</li>
* <li>KEM envelopes: {@link zeroecho.sdk.builders.alg.KemDataContentBuilder}

View File

@@ -85,8 +85,8 @@ public final class SecretPassword implements SecretContent, Destroyable {
}
/**
* Constructs a password from a caller-owned character array. The supplied
* array is cloned and remains owned by the caller.
* Constructs a password from a caller-owned character array. The supplied array
* is cloned and remains owned by the caller.
*
* @param password password characters; must not be {@code null}
* @throws NullPointerException if {@code password} is {@code null}

View File

@@ -148,8 +148,7 @@ final class Decryptor implements PlainContent, MultiRecipientContent {
} catch (AEADBadTagException ex) {
// wrong key/password for that entry, continue scanning
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "recipient authentication failed: {0}",
ex.getClass().getSimpleName());
LOG.log(Level.FINE, "recipient authentication failed: {0}", ex.getClass().getSimpleName());
}
} catch (GeneralSecurityException | IOException | IllegalArgumentException ex) {
// entry not applicable to this opener/material; ignore and continue
@@ -198,8 +197,7 @@ final class Decryptor implements PlainContent, MultiRecipientContent {
}
}
private void closeAfterAttempt(InputStream input, boolean transferred, Throwable primary)
throws IOException {
private void closeAfterAttempt(InputStream input, boolean transferred, Throwable primary) throws IOException {
IOException cleanupFailure = null;
if (!transferred) {
try {
@@ -282,8 +280,7 @@ final class Decryptor implements PlainContent, MultiRecipientContent {
closeOpeners(openers, primary);
}
/* default */ static void closeOpeners(List<RecipientOpener> ownedOpeners, Throwable primary)
throws IOException {
/* default */ static void closeOpeners(List<RecipientOpener> ownedOpeners, Throwable primary) throws IOException {
IOException cleanupFailure = null;
for (RecipientOpener opener : ownedOpeners) { // NOPMD - each opener is closed in this loop
try {
@@ -309,14 +306,13 @@ final class Decryptor implements PlainContent, MultiRecipientContent {
}
}
private void rejectOversizedCek(byte[] candidate, int fieldIndex, String recipientId,
RecipientOpener opener) {
private void rejectOversizedCek(byte[] candidate, int fieldIndex, String recipientId, RecipientOpener opener) {
try {
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING,
"Suspicious material in field {0}: {1}/{2} returned length {3}, while {4} is the limit. Ignoring.",
new Object[] { fieldIndex, recipientId, opener.getClass().getName(),
candidate.length, keyBytes });
new Object[] { fieldIndex, recipientId, opener.getClass().getName(), candidate.length,
keyBytes });
}
} finally {
Arrays.fill(candidate, (byte) 0);

View File

@@ -84,8 +84,7 @@ final class Encryptor implements EncryptedContent, MultiRecipientContent {
this.keyBytes = keyBytes;
this.maxRecipients = maxRecipients;
this.maxEntryLen = maxEntryLen;
this.randomBytesFactory = Objects.requireNonNull(randomBytesFactory,
"randomBytesFactory must not be null");
this.randomBytesFactory = Objects.requireNonNull(randomBytesFactory, "randomBytesFactory must not be null");
}
/**
@@ -232,8 +231,7 @@ final class Encryptor implements EncryptedContent, MultiRecipientContent {
return key;
}
/* default */ static void closeRecipients(List<Recipient> ownedRecipients, Throwable primary)
throws IOException {
/* default */ static void closeRecipients(List<Recipient> ownedRecipients, Throwable primary) throws IOException {
IOException cleanupFailure = null;
for (Recipient recipient : ownedRecipients) {
try {

View File

@@ -82,8 +82,7 @@ public final class KemCtxRecipient implements Recipient, AutoCloseable {
* @param kekBytes KEK length; exactly 16 or 32 bytes
* @param saltLen length of the random salt to apply during HKDF
* @throws NullPointerException if {@code ctx} is {@code null}
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or
* 32
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or 32
*/
public KemCtxRecipient(KemContext ctx, int kekBytes, int saltLen) {
this(ctx, kekBytes, saltLen, false);
@@ -107,8 +106,7 @@ public final class KemCtxRecipient implements Recipient, AutoCloseable {
* @param decoy {@code true} if this recipient is a decoy (fake entry that
* cannot unwrap a CEK); {@code false} if it is a real recipient
* @throws NullPointerException if {@code ctx} is {@code null}
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or
* 32
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or 32
*/
public KemCtxRecipient(KemContext ctx, int kekBytes, int saltLen, boolean decoy) {
int validatedKekBytes = RecipientKekSizes.requireSupported(kekBytes);

View File

@@ -23,9 +23,9 @@ final class KemKeyDerivation {
* Derives a KEK bound to the KEM algorithm identifier.
*
* @param sharedSecret KEM shared secret
* @param salt HKDF salt
* @param algorithmId canonical KEM algorithm identifier
* @param outputBytes requested KEK size
* @param salt HKDF salt
* @param algorithmId canonical KEM algorithm identifier
* @param outputBytes requested KEK size
* @return newly allocated KEK bytes
* @throws GeneralSecurityException if HKDF fails
*/

View File

@@ -46,17 +46,17 @@ import zeroecho.sdk.content.api.DataContent;
* resources until processing or explicit cleanup.
*
* <p>
* Callers must invoke {@link #close()} when a built instance is abandoned before
* {@link #getStream()} is called. Successful or failed stream construction also
* releases the owned recipient resources. Unlocking keys and password material
* supplied separately remain caller-owned and are never destroyed by this
* content.
* Callers must invoke {@link #close()} when a built instance is abandoned
* before {@link #getStream()} is called. Successful or failed stream
* construction also releases the owned recipient resources. Unlocking keys and
* password material supplied separately remain caller-owned and are never
* destroyed by this content.
* </p>
*
* <p>
* Implementations are not thread-safe. Cleanup is idempotent, and content cannot
* be used after cleanup. Calling {@link #getStream()} is terminal even when stream
* construction fails.
* Implementations are not thread-safe. Cleanup is idempotent, and content
* cannot be used after cleanup. Calling {@link #getStream()} is terminal even
* when stream construction fails.
* </p>
*/
public interface MultiRecipientContent extends DataContent, Destroyable, AutoCloseable {

View File

@@ -215,8 +215,8 @@ public final class MultiRecipientDataSourceBuilder
ensureOpen();
RecipientKekSizes.requireSupported(kekBytes);
session.pbkdf2Limits().validateTrusted(iterations);
this.recipients.add(new PasswordRecipient(password, iterations, saltLen, kekBytes, false,
session.pbkdf2Limits()));
this.recipients
.add(new PasswordRecipient(password, iterations, saltLen, kekBytes, false, session.pbkdf2Limits()));
return this;
}
@@ -236,8 +236,7 @@ public final class MultiRecipientDataSourceBuilder
* @param saltLen HKDF salt length in bytes
* @return this builder
* @throws NullPointerException if {@code kem} is {@code null}
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or
* 32
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or 32
*/
public MultiRecipientDataSourceBuilder addRecipient(KemContext kem, int kekBytes, int saltLen) {
ensureOpen();
@@ -292,8 +291,8 @@ public final class MultiRecipientDataSourceBuilder
ensureOpen();
RecipientKekSizes.requireSupported(kekBytes);
session.pbkdf2Limits().validateTrusted(iterations);
this.recipients.add(new PasswordRecipient(password, iterations, saltLen, kekBytes, true,
session.pbkdf2Limits()));
this.recipients
.add(new PasswordRecipient(password, iterations, saltLen, kekBytes, true, session.pbkdf2Limits()));
return this;
}
@@ -314,8 +313,7 @@ public final class MultiRecipientDataSourceBuilder
* @param saltLen HKDF salt length in bytes
* @return this builder
* @throws NullPointerException if {@code kem} is {@code null}
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or
* 32
* @throws IllegalArgumentException if {@code kekBytes} is not exactly 16 or 32
*/
public MultiRecipientDataSourceBuilder addRecipientDecoy(KemContext kem, int kekBytes, int saltLen) {
ensureOpen();
@@ -384,9 +382,9 @@ public final class MultiRecipientDataSourceBuilder
* </p>
*
* <p>
* The builder takes ownership of the opener. The opener must be reusable
* across all recipient entries and is closed after scanning or when the built
* content is abandoned.
* The builder takes ownership of the opener. The opener must be reusable across
* all recipient entries and is closed after scanning or when the built content
* is abandoned.
* </p>
*
* @param opener reusable opener to add
@@ -479,8 +477,10 @@ public final class MultiRecipientDataSourceBuilder
/**
* Destroys recipient secrets still owned by this builder.
*
* <p>Recipients transferred to a successfully built encrypting content object
* are owned and destroyed by that object instead.</p>
* <p>
* Recipients transferred to a successfully built encrypting content object are
* owned and destroyed by that object instead.
* </p>
*
* @throws DestroyFailedException if recipient cleanup fails
*/

View File

@@ -58,6 +58,7 @@ public final class PasswordOpener implements RecipientOpener {
public PasswordOpener(Pbkdf2Limits limits) {
this.limits = java.util.Objects.requireNonNull(limits, "limits must not be null");
}
/**
* Attempts to open a password-based recipient entry using a password unlock
* material.

View File

@@ -76,8 +76,8 @@ public final class PasswordRecipient implements Recipient, Destroyable, AutoClos
* <li>The caller should clear the {@code password} array after constructing the
* recipient to minimize exposure in memory.</li>
* <li>Choose an iteration count appropriate to the target platform to balance
* password-guessing resistance against recipient creation and opening
* latency. Counts below {@value Pbkdf2Limits#MINIMUM} are rejected.</li>
* password-guessing resistance against recipient creation and opening latency.
* Counts below {@value Pbkdf2Limits#MINIMUM} are rejected.</li>
* <li>Decoy recipients increase confidentiality by hiding the number of real
* recipients but cannot successfully unwrap the CEK.</li>
* </ul>

View File

@@ -8,9 +8,11 @@ package zeroecho.sdk.guard;
* Defines the KEK sizes supported by recipient entries without an encoded size
* discriminator.
*
* <p>The current recipient format permits AES-128 and AES-256 wrapping only.
* <p>
* The current recipient format permits AES-128 and AES-256 wrapping only.
* Validation must occur before an entry is registered so every emitted entry
* remains openable by the corresponding recipient opener.</p>
* remains openable by the corresponding recipient opener.
* </p>
*
* @since 1.0
*/

View File

@@ -18,9 +18,11 @@ import zeroecho.core.annotation.Describable;
/**
* Caller-owned session-operation input used to unlock a recipient entry.
*
* <p>Components accepting an {@code UnlockMaterial} borrow it and do not destroy
* <p>
* Components accepting an {@code UnlockMaterial} borrow it and do not destroy
* it. The caller must keep it usable until the operation completes and destroy
* password material afterwards.</p>
* password material afterwards.
* </p>
*/
public sealed interface UnlockMaterial extends Describable {
/**
@@ -44,8 +46,10 @@ public sealed interface UnlockMaterial extends Describable {
/**
* Destroyable password unlocking material backed by an owned character array.
*
* <p>Construction and access use defensive copies. Destruction is idempotent
* and prevents subsequent access.</p>
* <p>
* Construction and access use defensive copies. Destruction is idempotent and
* prevents subsequent access.
* </p>
*/
final class Password implements UnlockMaterial, Destroyable {
private final char[] characters;

View File

@@ -108,8 +108,8 @@
* generation and recipient entries; the symmetric builder manages algorithm
* parameters and payload framing.</li>
* <li><strong>Reusable opener strategies:</strong> recipients encode entries;
* openers attempt every applicable entry and create fresh cryptographic contexts
* per attempt. Neither carries long-lived secret state.</li>
* openers attempt every applicable entry and create fresh cryptographic
* contexts per attempt. Neither carries long-lived secret state.</li>
* <li><strong>Defensive parsing:</strong> the builder applies limits to the
* number of recipients and the size of each entry blob; the symmetric stage
* applies its own limits to its header and payload.</li>

View File

@@ -196,8 +196,8 @@ public final class HybridDerived {
* construction.
* </p>
*
* @param aes AES builder to configure (must not be null)
* @param keyBits AES key size in bits (128/192/256)
* @param aes AES builder to configure (must not be null)
* @param keyBits AES key size in bits (128/192/256)
* @return the provided builder instance
* @throws NullPointerException if aes is null
* @throws IllegalArgumentException if keyBits is invalid
@@ -225,16 +225,16 @@ public final class HybridDerived {
}
/**
* Derives a ChaCha key and applies it with optional AAD to the provided
* ChaCha builder.
* Derives a ChaCha key and applies it with optional AAD to the provided ChaCha
* builder.
*
* <p>
* The returned value is the same builder instance to preserve fluent pipeline
* construction.
* </p>
*
* @param chacha ChaCha builder to configure (must not be null)
* @param keyBits key size in bits (typically 256)
* @param chacha ChaCha builder to configure (must not be null)
* @param keyBits key size in bits (typically 256)
* @return the provided builder instance
* @throws NullPointerException if chacha is null
* @throws IllegalArgumentException if keyBits is invalid

View File

@@ -150,8 +150,8 @@ public final class HybridKexContext implements MessageAgreementContext {
this(profile, classic, pqc, Kdf::hkdfSha256);
}
/* default */ HybridKexContext(HybridKexProfile profile, AgreementContext classic,
MessageAgreementContext pqc, SecretDeriver secretDeriver) {
/* default */ HybridKexContext(HybridKexProfile profile, AgreementContext classic, MessageAgreementContext pqc,
SecretDeriver secretDeriver) {
this.profile = Objects.requireNonNull(profile, "profile");
this.classic = Objects.requireNonNull(classic, "classic");
this.pqc = Objects.requireNonNull(pqc, "pqc");
@@ -358,15 +358,14 @@ public final class HybridKexContext implements MessageAgreementContext {
/**
* Derives the final output from owned temporary hybrid input.
*
* @param ikm combined component secrets
* @param salt HKDF salt
* @param info HKDF context information
* @param ikm combined component secrets
* @param salt HKDF salt
* @param info HKDF context information
* @param outputLength requested output length
* @return derived output transferred to the caller
* @throws GeneralSecurityException if derivation fails
*/
byte[] derive(byte[] ikm, byte[] salt, byte[] info, int outputLength)
throws GeneralSecurityException;
byte[] derive(byte[] ikm, byte[] salt, byte[] info, int outputLength) throws GeneralSecurityException;
}
/**

View File

@@ -83,9 +83,11 @@ import zeroecho.sdk.ZeroEchoSession;
* </p>
*
* <h2>Error handling</h2>
* <p>Context construction is in-memory and reports configuration or provider
* failures through the security exception model of {@link ZeroEchoSession}.
* I/O failures remain associated with later stream processing.</p>
* <p>
* Context construction is in-memory and reports configuration or provider
* failures through the security exception model of {@link ZeroEchoSession}. I/O
* failures remain associated with later stream processing.
* </p>
*
* <h2>Thread safety</h2>
* <p>
@@ -158,8 +160,7 @@ public final class HybridKexContexts {
AgreementContext classic = null;
MessageAgreementContext pqc = null;
try {
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicInitiatorPrivate,
classicSpec);
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicInitiatorPrivate, classicSpec);
classic.setPeerPublic(classicPeerPublic);
pqc = session.createContext(pqcAlgId, KeyUsage.AGREEMENT, pqcPeerPublic, pqcSpec);
return new HybridKexContext(profile, classic, pqc);
@@ -222,8 +223,7 @@ public final class HybridKexContexts {
AgreementContext classic = null;
MessageAgreementContext pqc = null;
try {
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicResponderPrivate,
classicSpec);
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicResponderPrivate, classicSpec);
classic.setPeerPublic(classicPeerPublic);
pqc = session.createContext(pqcAlgId, KeyUsage.AGREEMENT, pqcResponderPrivate, pqcSpec);
return new HybridKexContext(profile, classic, pqc);
@@ -262,9 +262,8 @@ public final class HybridKexContexts {
* @throws NullPointerException if any required argument is {@code null}
*/
public static HybridKexContext initiatorPairMessage(ZeroEchoSession session, HybridKexProfile profile,
String classicAlgId,
zeroecho.core.alg.common.agreement.KeyPairKey classicInitiatorKeyPair, ContextSpec classicSpec,
String pqcAlgId, PublicKey pqcPeerPublic, ContextSpec pqcSpec) {
String classicAlgId, zeroecho.core.alg.common.agreement.KeyPairKey classicInitiatorKeyPair,
ContextSpec classicSpec, String pqcAlgId, PublicKey pqcPeerPublic, ContextSpec pqcSpec) {
Objects.requireNonNull(session, "session");
Objects.requireNonNull(profile, "profile");
@@ -276,8 +275,7 @@ public final class HybridKexContexts {
MessageAgreementContext classic = null;
MessageAgreementContext pqc = null;
try {
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicInitiatorKeyPair,
classicSpec);
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicInitiatorKeyPair, classicSpec);
pqc = session.createContext(pqcAlgId, KeyUsage.AGREEMENT, pqcPeerPublic, pqcSpec);
return new HybridKexContext(profile, classic, pqc);
} catch (RuntimeException | Error failure) { // NOPMD - close partial construction
@@ -312,9 +310,8 @@ public final class HybridKexContexts {
* @throws NullPointerException if any required argument is {@code null}
*/
public static HybridKexContext responderPairMessage(ZeroEchoSession session, HybridKexProfile profile,
String classicAlgId,
zeroecho.core.alg.common.agreement.KeyPairKey classicResponderKeyPair, ContextSpec classicSpec,
String pqcAlgId, PrivateKey pqcResponderPrivate, ContextSpec pqcSpec) {
String classicAlgId, zeroecho.core.alg.common.agreement.KeyPairKey classicResponderKeyPair,
ContextSpec classicSpec, String pqcAlgId, PrivateKey pqcResponderPrivate, ContextSpec pqcSpec) {
Objects.requireNonNull(session, "session");
Objects.requireNonNull(profile, "profile");
@@ -326,8 +323,7 @@ public final class HybridKexContexts {
MessageAgreementContext classic = null;
MessageAgreementContext pqc = null;
try {
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicResponderKeyPair,
classicSpec);
classic = session.createContext(classicAlgId, KeyUsage.AGREEMENT, classicResponderKeyPair, classicSpec);
pqc = session.createContext(pqcAlgId, KeyUsage.AGREEMENT, pqcResponderPrivate, pqcSpec);
return new HybridKexContext(profile, classic, pqc);
} catch (RuntimeException | Error failure) { // NOPMD - close partial construction

View File

@@ -155,8 +155,10 @@ public final class HybridKexExporter implements Destroyable, AutoCloseable {
/**
* Overwrites the exporter root secret and salt.
*
* <p>Destruction is idempotent. All subsequent export or diagnostic access
* fails with {@link IllegalStateException}.</p>
* <p>
* Destruction is idempotent. All subsequent export or diagnostic access fails
* with {@link IllegalStateException}.
* </p>
*/
@Override
public void destroy() {

View File

@@ -118,8 +118,7 @@ final class HybridSignatureContext implements SignatureContext {
* @throws IllegalArgumentException if {@code maxBufferedBytes <= 0}
*/
/* default */ HybridSignatureContext(ZeroEchoSession session, HybridSignatureProfile profile,
PrivateKey classicPrivate,
PrivateKey pqcPrivate, int maxBufferedBytes) {
PrivateKey classicPrivate, PrivateKey pqcPrivate, int maxBufferedBytes) {
this.session = Objects.requireNonNull(session, "session");
this.profile = Objects.requireNonNull(profile, "profile");
this.classicPrivate = Objects.requireNonNull(classicPrivate, "classicPrivate");
@@ -151,8 +150,7 @@ final class HybridSignatureContext implements SignatureContext {
* @throws IllegalArgumentException if {@code maxBufferedBytes <= 0}
*/
/* default */ HybridSignatureContext(ZeroEchoSession session, HybridSignatureProfile profile,
PublicKey classicPublic,
PublicKey pqcPublic, int maxBufferedBytes) {
PublicKey classicPublic, PublicKey pqcPublic, int maxBufferedBytes) {
this.session = Objects.requireNonNull(session, "session");
this.profile = Objects.requireNonNull(profile, "profile");
this.classicPublic = Objects.requireNonNull(classicPublic, "classicPublic");

View File

@@ -69,8 +69,7 @@ public final class HybridSignatureContexts {
* @since 1.0
*/
public static SignatureContext sign(ZeroEchoSession session, HybridSignatureProfile profile,
PrivateKey classicPrivate,
PrivateKey pqcPrivate, int maxBufferedBytes) {
PrivateKey classicPrivate, PrivateKey pqcPrivate, int maxBufferedBytes) {
Objects.requireNonNull(session, "session");
Objects.requireNonNull(profile, "profile");
Objects.requireNonNull(classicPrivate, "classicPrivate");

View File

@@ -35,7 +35,6 @@ package zeroecho.sdk.util;
import zeroecho.core.util.RandomSupport;
/**
* Utility class for generating random passwords and secure random byte arrays.
* <p>

View File

@@ -74,9 +74,8 @@ class CapabilityValueSemanticsTest {
void nullAndIncompatibleDefaultsAreRejectedAtConstruction() {
System.out.println("nullAndIncompatibleDefaultsAreRejectedAtConstruction");
assertThrows(NullPointerException.class, () -> capability(() -> null));
assertThrows(IllegalArgumentException.class,
() -> new Capability("DIGEST", AlgorithmFamily.DIGEST, KeyUsage.DIGEST, DigestContext.class,
NullKey.class, TestSpec.class, new OtherSpec()));
assertThrows(IllegalArgumentException.class, () -> new Capability("DIGEST", AlgorithmFamily.DIGEST,
KeyUsage.DIGEST, DigestContext.class, NullKey.class, TestSpec.class, new OtherSpec()));
System.out.println("...invalidDefaultsRejected=true");
System.out.println("nullAndIncompatibleDefaultsAreRejectedAtConstruction...ok");
@@ -121,8 +120,7 @@ class CapabilityValueSemanticsTest {
(key, spec) -> {
runtimeSpecs.add(spec);
return mock(DigestContext.class);
},
() -> new TestSpec(Integer.toString(evaluations.incrementAndGet())));
}, () -> new TestSpec(Integer.toString(evaluations.incrementAndGet())));
}
}
}

View File

@@ -105,9 +105,10 @@ public class CatalogContractTest {
Logger jul = Logger.getLogger("zeroecho.audit");
jul.setLevel(Level.FINE); // see PROGRESS at FINE
session = new ZeroEchoSession().withAuditListener(JulAuditListenerStd.builder().logger(jul)
.infoLevel(Level.INFO).warnLevel(Level.WARNING).progressLevel(Level.FINE)
.includeStackTraces(true).build()).withAuditMode(AuditMode.WRAP);
session = new ZeroEchoSession()
.withAuditListener(JulAuditListenerStd.builder().logger(jul).infoLevel(Level.INFO)
.warnLevel(Level.WARNING).progressLevel(Level.FINE).includeStackTraces(true).build())
.withAuditMode(AuditMode.WRAP);
dump("");
dump("zeroecho.core.audit");
@@ -178,8 +179,7 @@ public class CatalogContractTest {
.anyMatch(info -> info.operation() == KeyOperation.SYMMETRIC_GENERATE);
// SIGN/VERIFY (asymmetric)
if (alg.roles().contains(KeyUsage.SIGN) && alg.roles().contains(KeyUsage.VERIFY)
&& hasAsym) {
if (alg.roles().contains(KeyUsage.SIGN) && alg.roles().contains(KeyUsage.VERIFY) && hasAsym) {
trySignVerify(id, msg);
System.out.println();
}
@@ -193,8 +193,7 @@ public class CatalogContractTest {
}
// KEM
if (alg.roles().contains(KeyUsage.ENCAPSULATE) && alg.roles().contains(KeyUsage.DECAPSULATE)
&& hasAsym) {
if (alg.roles().contains(KeyUsage.ENCAPSULATE) && alg.roles().contains(KeyUsage.DECAPSULATE) && hasAsym) {
tryKem(id, msg);
System.out.println();
}

View File

@@ -74,10 +74,10 @@ import zeroecho.core.tag.TagEngine;
*
* <p>
* These tests focus on the internal audit wrapping path used by
* {@link AuditedContexts#wrap(CryptoContext, AuditListener, KeyUsage)}.
* They verify that representative context types are wrapped as audited JDK
* proxies and that the resulting wrapper preserves the expected basic
* delegation behavior.
* {@link AuditedContexts#wrap(CryptoContext, AuditListener, KeyUsage)}. They
* verify that representative context types are wrapped as audited JDK proxies
* and that the resulting wrapper preserves the expected basic delegation
* behavior.
* </p>
*/
class CryptoAlgorithmsAuditWrapTest {

View File

@@ -27,7 +27,8 @@ import zeroecho.core.context.DigestContext;
import zeroecho.sdk.ZeroEchoSession;
/**
* Verifies authoritative registry ownership and explicitly scoped runtime state.
* Verifies authoritative registry ownership and explicitly scoped runtime
* state.
*/
class CryptoArchitectureTest {
@@ -51,8 +52,8 @@ class CryptoArchitectureTest {
System.out.println("explicitPolicyOrder");
List<String> events = new ArrayList<>();
AuditListener listener = policyOrderListener(events);
ZeroEchoSession allowed = new ZeroEchoSession().withAuditListener(listener).withPolicy(
(id, role, key, spec) -> events.add("policy"));
ZeroEchoSession allowed = new ZeroEchoSession().withAuditListener(listener)
.withPolicy((id, role, key, spec) -> events.add("policy"));
try (DigestContext context = allowed.createContext("DIGEST", KeyUsage.DIGEST, NullKey.INSTANCE)) {
assertSame(CryptoAlgorithms.require("DIGEST"), context.algorithm());
}
@@ -60,19 +61,18 @@ class CryptoArchitectureTest {
events.clear();
IllegalArgumentException denial = new IllegalArgumentException("controlled denial");
ZeroEchoSession denied = new ZeroEchoSession().withAuditListener(listener).withPolicy(
(id, role, key, spec) -> {
events.add("policy");
throw denial;
});
ZeroEchoSession denied = new ZeroEchoSession().withAuditListener(listener).withPolicy((id, role, key, spec) -> {
events.add("policy");
throw denial;
});
assertSame(denial, assertThrows(IllegalArgumentException.class,
() -> denied.createContext("DIGEST", KeyUsage.DIGEST, NullKey.INSTANCE)));
assertEquals(List.of("policy"), events);
events.clear();
IllegalStateException failure = new IllegalStateException("controlled policy failure");
ZeroEchoSession failing = new ZeroEchoSession().withAuditListener(listener).withPolicy(
(id, role, key, spec) -> {
ZeroEchoSession failing = new ZeroEchoSession().withAuditListener(listener)
.withPolicy((id, role, key, spec) -> {
events.add("policy");
throw failure;
});
@@ -91,8 +91,7 @@ class CryptoArchitectureTest {
AtomicInteger secondEvents = new AtomicInteger();
AuditListener firstListener = contextListener(firstEvents);
AuditListener secondListener = contextListener(secondEvents);
ZeroEchoSession wrapped = new ZeroEchoSession().withAuditListener(firstListener)
.withAuditMode(AuditMode.WRAP);
ZeroEchoSession wrapped = new ZeroEchoSession().withAuditListener(firstListener).withAuditMode(AuditMode.WRAP);
ZeroEchoSession direct = new ZeroEchoSession().withAuditListener(secondListener);
try (DigestContext wrappedContext = wrapped.createContext("DIGEST", KeyUsage.DIGEST, NullKey.INSTANCE)) {

View File

@@ -58,15 +58,12 @@ class SecretSpecLifecycleTest {
new SpecCase("zeroecho.core.alg.kyber.KyberPrivateKeySpec", "pkcs8", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.mldsa.MldsaPrivateKeySpec", "encoded", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.ntru.NtruPrivateKeySpec", "pkcs8", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.ntruprime.NtrulPrimePrivateKeySpec", "pkcs8", 8,
Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.ntruprime.SntruPrimePrivateKeySpec", "pkcs8", 8,
Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.ntruprime.NtrulPrimePrivateKeySpec", "pkcs8", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.ntruprime.SntruPrimePrivateKeySpec", "pkcs8", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.rsa.RsaPrivateKeySpec", "encoded", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.saber.SaberPrivateKeySpec", "pkcs8", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.slhdsa.SlhDsaPrivateKeySpec", "encoded", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.sphincsplus.SphincsPlusPrivateKeySpec", "encoded", 8,
Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.sphincsplus.SphincsPlusPrivateKeySpec", "encoded", 8, Factory.CONSTRUCTOR),
new SpecCase("zeroecho.core.alg.xdh.XdhPrivateKeySpec", "encoded", 8, Factory.CONSTRUCTOR));
@Test
@@ -131,14 +128,14 @@ class SecretSpecLifecycleTest {
() -> AesKeyImportSpec.unmarshal(PairSeq.of("k.b64", aesKey, "k.b64", "%")));
assertThrows(IllegalArgumentException.class,
() -> ChaChaKeyImportSpec.unmarshal(PairSeq.of("k.b64", chachaKey, "k.b64", "%")));
assertThrows(IllegalArgumentException.class, () -> HmacKeyImportSpec.unmarshal(
PairSeq.of("mac", "HmacSHA256", "k.b64", encodedPrivateKey, "k.b64", "%")));
assertThrows(IllegalArgumentException.class, () -> MldsaPrivateKeySpec.unmarshal(
PairSeq.of("pkcs8.b64", encodedPrivateKey, "pkcs8.b64", "%")));
assertThrows(IllegalArgumentException.class, () -> SlhDsaPrivateKeySpec.unmarshal(
PairSeq.of("pkcs8.b64", encodedPrivateKey, "pkcs8.b64", "%")));
assertThrows(IllegalArgumentException.class, () -> SphincsPlusPrivateKeySpec.unmarshal(
PairSeq.of("pkcs8.b64", encodedPrivateKey, "pkcs8.b64", "%")));
assertThrows(IllegalArgumentException.class, () -> HmacKeyImportSpec
.unmarshal(PairSeq.of("mac", "HmacSHA256", "k.b64", encodedPrivateKey, "k.b64", "%")));
assertThrows(IllegalArgumentException.class,
() -> MldsaPrivateKeySpec.unmarshal(PairSeq.of("pkcs8.b64", encodedPrivateKey, "pkcs8.b64", "%")));
assertThrows(IllegalArgumentException.class,
() -> SlhDsaPrivateKeySpec.unmarshal(PairSeq.of("pkcs8.b64", encodedPrivateKey, "pkcs8.b64", "%")));
assertThrows(IllegalArgumentException.class, () -> SphincsPlusPrivateKeySpec
.unmarshal(PairSeq.of("pkcs8.b64", encodedPrivateKey, "pkcs8.b64", "%")));
System.out.println("...cases=6");
System.out.println("unmarshalCleansUpWhenMalformedDataFollowsValidSecretMaterial...ok");
}
@@ -195,7 +192,8 @@ class SecretSpecLifecycleTest {
generator.initialize(2048);
KeyPair pair = generator.generateKeyPair();
RsaPrivateKeySpec rsaSpec = new RsaPrivateKeySpec(pair.getPrivate().getEncoded());
PrivateKey imported = new zeroecho.sdk.ZeroEchoSession().keyBuilders().asymmetric().importPrivate("RSA", rsaSpec);
PrivateKey imported = new zeroecho.sdk.ZeroEchoSession().keyBuilders().asymmetric().importPrivate("RSA",
rsaSpec);
assertArrayEquals(pair.getPrivate().getEncoded(), imported.getEncoded());
assertFalse(rsaSpec.isDestroyed());
assertArrayEquals(pair.getPrivate().getEncoded(), rsaSpec.encoded());
@@ -221,9 +219,7 @@ class SecretSpecLifecycleTest {
}
private enum Factory {
CONSTRUCTOR,
STATIC_RAW,
HMAC
CONSTRUCTOR, STATIC_RAW, HMAC
}
private record SpecCase(String className, String accessor, int length, Factory factory) {

Some files were not shown because too many files have changed in this diff Show More