From f4dd149c52c12ed77750134a3c029b73ff05b91e Mon Sep 17 00:00:00 2001 From: Leumor <116955025+leumor@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:31:38 +0000 Subject: [PATCH] refactor(keys): split CHK encode params Separate payload vs algorithm parameters for CHK encoding and update call sites/tests.\nAdd value-object docs and Sonar suppressions for immutable carriers. --- .../onionnetworks/fec/MatrixMulParams.java | 1 + .../crypta/client/MetadataTopLayerInfo.java | 1 + .../crypta/client/SplitfileParams.java | 1 + .../client/async/ClientPutterOptions.java | 1 + .../client/async/CompressionOutput.java | 1 + .../client/async/InsertExecutionOptions.java | 1 + .../client/async/ManifestPutterParams.java | 1 + .../client/async/SingleFileInserter.java | 1 + .../SplitFileFetcherSegmentsLoadParams.java | 1 + .../SplitFileFetcherStoragePersistence.java | 1 + .../SplitFileFetcherStorageSettingsCodec.java | 1 + .../crypta/client/async/USKFoundEdition.java | 1 + .../client/filter/CSSTokenizerFilter.java | 1 + .../client/filter/FilterMIMETypeNames.java | 1 + .../crypta/client/filter/FlacFilter.java | 1 + .../clients/fcp/ClientGetStatusSnapshot.java | 229 ++++++++++++++++-- .../fcp/ClientPutDiskUploadValidator.java | 1 + .../fcp/DownloadRequestStatusDetails.java | 1 + .../fcp/PersistentPutRequestMetadata.java | 1 + .../clients/http/ConnectionsToadlet.java | 1 + .../crypta/clients/http/QueueToadlet.java | 6 + .../network/crypta/keys/ClientCHKBlock.java | 22 +- .../keys/ClientCHKEncodeAlgorithms.java | 30 +++ .../crypta/keys/ClientCHKEncodeParams.java | 226 ++++++++++++----- .../crypta/keys/ClientCHKEncodePayload.java | 176 ++++++++++++++ .../java/network/crypta/keys/FreenetURI.java | 2 + src/main/java/network/crypta/keys/Key.java | 1 + .../crypta/node/MessageFragmentPayload.java | 1 + src/main/java/network/crypta/node/Node.java | 1 + .../crypta/node/PeerRoutingSelector.java | 1 + .../crypta/node/SessionKeyCryptoMaterial.java | 1 + .../network/crypta/store/StoreCallback.java | 1 + .../saltedhash/SaltedHashFreenetStore.java | 2 + .../saltedhash/SaltedHashStoreParams.java | 1 + .../ode/RungeKuttaFehlbergIntegrator.java | 1 + .../ode/RungeKuttaFehlbergMethod.java | 1 + .../ode/StepInitializationContext.java | 1 + .../mantissa/optimization/PointCostPair.java | 1 + .../crypta/keys/ClientCHKBlockTest.java | 28 +-- 39 files changed, 635 insertions(+), 116 deletions(-) create mode 100644 src/main/java/network/crypta/keys/ClientCHKEncodeAlgorithms.java create mode 100644 src/main/java/network/crypta/keys/ClientCHKEncodePayload.java diff --git a/src/main/java/com/onionnetworks/fec/MatrixMulParams.java b/src/main/java/com/onionnetworks/fec/MatrixMulParams.java index 3c9e5fe8dbc..a488a0fbbff 100644 --- a/src/main/java/com/onionnetworks/fec/MatrixMulParams.java +++ b/src/main/java/com/onionnetworks/fec/MatrixMulParams.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; /** Parameter carrier for finite-field matrix multiplication slices. */ +@SuppressWarnings("java:S6206") public final class MatrixMulParams { private final char[] a; private final int aStart; diff --git a/src/main/java/network/crypta/client/MetadataTopLayerInfo.java b/src/main/java/network/crypta/client/MetadataTopLayerInfo.java index d3d4dd96f8b..9ee4956598f 100644 --- a/src/main/java/network/crypta/client/MetadataTopLayerInfo.java +++ b/src/main/java/network/crypta/client/MetadataTopLayerInfo.java @@ -33,6 +33,7 @@ * @see MetadataRedirectTarget * @see SplitfilePayload */ +@SuppressWarnings("java:S6206") public final class MetadataTopLayerInfo { private final long origDataLength; private final long origCompressedDataLength; diff --git a/src/main/java/network/crypta/client/SplitfileParams.java b/src/main/java/network/crypta/client/SplitfileParams.java index 32bc79ee654..a698abf5ea8 100644 --- a/src/main/java/network/crypta/client/SplitfileParams.java +++ b/src/main/java/network/crypta/client/SplitfileParams.java @@ -32,6 +32,7 @@ * @see SplitfilePayload * @see MetadataTopLayerInfo */ +@SuppressWarnings("java:S6206") public final class SplitfileParams { private final SplitfileAlgorithm splitfileAlgorithm; private final ClientCHK[] dataURIs; diff --git a/src/main/java/network/crypta/client/async/ClientPutterOptions.java b/src/main/java/network/crypta/client/async/ClientPutterOptions.java index 2ceff44b221..61ad06e9117 100644 --- a/src/main/java/network/crypta/client/async/ClientPutterOptions.java +++ b/src/main/java/network/crypta/client/async/ClientPutterOptions.java @@ -26,6 +26,7 @@ *
The {@code settingsStream} remains open and positioned at the first byte after the parsed * fields so callers can continue reading segment metadata without reparsing the buffer. */ +@SuppressWarnings("java:S6206") final class ParsedBasicSettings { private final SplitfileAlgorithm splitfileType; private final byte splitfileSingleCryptoAlgorithm; diff --git a/src/main/java/network/crypta/client/async/USKFoundEdition.java b/src/main/java/network/crypta/client/async/USKFoundEdition.java index d82f41fb5ba..fa4e809892c 100644 --- a/src/main/java/network/crypta/client/async/USKFoundEdition.java +++ b/src/main/java/network/crypta/client/async/USKFoundEdition.java @@ -12,6 +12,7 @@ * fetchers and subscriptions. It is shared across callbacks to avoid long parameter lists and to * keep related data coherent during async handoff. */ +@SuppressWarnings("java:S6206") public final class USKFoundEdition { private final long edition; private final USK key; diff --git a/src/main/java/network/crypta/client/filter/CSSTokenizerFilter.java b/src/main/java/network/crypta/client/filter/CSSTokenizerFilter.java index 98cebe17896..0847d8fd65e 100644 --- a/src/main/java/network/crypta/client/filter/CSSTokenizerFilter.java +++ b/src/main/java/network/crypta/client/filter/CSSTokenizerFilter.java @@ -6526,6 +6526,7 @@ private VariableOccurrenceLimits withBounds(int newLowerLimit, int newUpperLimit } /** Parameters for verifying parse expressions that use the {@code []} repetition operator. */ + @SuppressWarnings("java:S6206") private static final class VariableOccurrenceParams { private final int verifierIndex; private final ParsedWord[] valueParts; diff --git a/src/main/java/network/crypta/client/filter/FilterMIMETypeNames.java b/src/main/java/network/crypta/client/filter/FilterMIMETypeNames.java index 8b1850343da..3e1c0aeb7cc 100644 --- a/src/main/java/network/crypta/client/filter/FilterMIMETypeNames.java +++ b/src/main/java/network/crypta/client/filter/FilterMIMETypeNames.java @@ -10,6 +10,7 @@ *
This value object bundles the canonical type string with any alternate type aliases and * filename extensions so callers can pass a single immutable object when registering handlers. */ +@SuppressWarnings("java:S6206") public final class FilterMIMETypeNames { private final String primaryMimeType; private final String primaryExtension; diff --git a/src/main/java/network/crypta/client/filter/FlacFilter.java b/src/main/java/network/crypta/client/filter/FlacFilter.java index 890884a9918..bd0d65a46ca 100644 --- a/src/main/java/network/crypta/client/filter/FlacFilter.java +++ b/src/main/java/network/crypta/client/filter/FlacFilter.java @@ -57,6 +57,7 @@ enum State { STREAM_FINISHED } + @SuppressWarnings("java:S6206") private static final class AudioReadResult { private final byte[] payload; private final short nextFrameHeader; diff --git a/src/main/java/network/crypta/clients/fcp/ClientGetStatusSnapshot.java b/src/main/java/network/crypta/clients/fcp/ClientGetStatusSnapshot.java index ba629557160..4f5bc559e4b 100644 --- a/src/main/java/network/crypta/clients/fcp/ClientGetStatusSnapshot.java +++ b/src/main/java/network/crypta/clients/fcp/ClientGetStatusSnapshot.java @@ -11,10 +11,28 @@ /** * Parameter bundle describing a {@link ClientGet} request status snapshot. * - *
This record mirrors the inputs required to construct a {@link DownloadRequestStatus} from the + *
This type captures the data required to construct a {@link DownloadRequestStatus} from the * current state of a {@link ClientGet} request. It is intentionally immutable and performs no - * validation, so callers can reuse it across status refreshes without altering behavior. + * validation, so callers can reuse it across status refreshes without altering behavior. Typical + * usage is to assemble one instance when a status reply is being generated, then pass it through + * the formatting layer that emits the FCP response. + * + *
All fields are stored verbatim, including arrays and buckets; no defensive copies are made. + * Callers should therefore treat contained arrays and referenced objects as read-only for the + * lifetime of the snapshot. Equality and hash code calculations include array contents, so mutating + * arrays after construction can destabilize hashing and comparisons. The instance is thread-safe + * only if all referenced objects are used in a thread-safe manner by the caller. + * + *
This constructor stores the supplied values without validation or copying. It is intended to + * be called at the moment a status response is prepared so the snapshot reflects a coherent view + * of the request. All parameters are optional in the sense that {@code null} values are accepted + * for reference types; callers should pass {@code null} only when the data is unknown or not + * applicable to the request type. + * + * @param identifier request identifier to report, typically unique per client session + * @param persistence persistence mode for the request, reflecting its configured lifetime + * @param started whether the request has started executing or queued work + * @param finished whether the request has completed, regardless of success + * @param succeeded whether the request has completed successfully, when finished is true + * @param progressPending last recorded progress snapshot, or {@code null} if none + * @param failedMessage cached failure message, or {@code null} when no failure is known + * @param foundDataMimeType MIME type discovered for the data, or {@code null} if unknown + * @param foundDataLength data length recorded for the request, in bytes when known + * @param destinationFile destination file for disk requests, or {@code null} for in-memory data + * @param dataBucket bucket containing result data, or {@code null} when not yet available + * @param fetchContext fetch context providing filter and MIME overrides, or {@code null} + * @param priorityClass scheduler priority class used by the request queue + * @param compatModes compatibility modes observed for the request, possibly empty or null + * @param splitfileKey splitfile crypto key override, or {@code null} if not applicable + * @param uri request URI to report, or {@code null} when not yet resolved + * @param dontCompress whether reinsertion should skip compression when producing output */ public ClientGetStatusSnapshot( String identifier, @@ -92,74 +116,223 @@ public ClientGetStatusSnapshot( this.dontCompress = dontCompress; } + /** + * Returns the request identifier reported to the client. + * + *
The identifier is typically client-provided and used to correlate status responses with the + * initiating request. It is stored verbatim and may be {@code null} when not available. + * + * @return the request identifier, or {@code null} if not set + */ public String identifier() { return identifier; } + /** + * Returns the persistence mode for the request. + * + *
The persistence value indicates how the request survives restarts or disconnects. This + * snapshot does not interpret the value and merely reports it back to the status encoder. + * + * @return the persistence mode, or {@code null} if not specified + */ public ClientRequest.Persistence persistence() { return persistence; } + /** + * Indicates whether the request has started. + * + *
This flag captures the most recent start state at snapshot creation time. It does not imply + * completion, and it may remain {@code true} throughout the entire request lifetime. + * + * @return {@code true} if the request has started, {@code false} otherwise + */ public boolean started() { return started; } + /** + * Indicates whether the request has finished. + * + *
A finished request has reached a terminal state. When this value is {@code true}, the {@link + * #succeeded()} flag describes whether completion was successful. + * + * @return {@code true} if the request has finished, {@code false} otherwise + */ public boolean finished() { return finished; } + /** + * Indicates whether the request has succeeded. + * + *
This flag is meaningful primarily when {@link #finished()} is {@code true}. When the request + * fails, the failure details are usually reported through {@link #failedMessage()}. + * + * @return {@code true} if the request succeeded, {@code false} otherwise + */ public boolean succeeded() { return succeeded; } + /** + * Returns the most recently recorded progress message, if any. + * + *
The progress snapshot is an optional view of in-flight work and may be {@code null} when no + * progress has been recorded or when the request has already completed. + * + * @return the last progress message, or {@code null} if none is available + */ public SimpleProgressMessage progressPending() { return progressPending; } + /** + * Returns the cached failure message, if the request failed. + * + *
This value is {@code null} when no failure is known. The snapshot does not derive or + * validate failure details; it simply stores and returns the provided message object. + * + * @return the failure message, or {@code null} if the request has not failed + */ public GetFailedMessage failedMessage() { return failedMessage; } + /** + * Returns the MIME type discovered for the data. + * + *
The MIME type is a hint derived from request processing and may be {@code null} when not yet + * known. The value is not normalized or validated by this snapshot. + * + * @return the discovered MIME type, or {@code null} if unknown + */ public String foundDataMimeType() { return foundDataMimeType; } + /** + * Returns the recorded data length for the request. + * + *
The length is expressed in bytes and reflects the latest known size at snapshot creation + * time. A value of {@code 0} may indicate unknown length or a zero-length payload depending on + * the calling context. + * + * @return the recorded data length in bytes + */ public long foundDataLength() { return foundDataLength; } + /** + * Returns the destination file for disk-based requests. + * + *
The file reference is optional and may be {@code null} for in-memory requests or when the + * destination has not been assigned. The snapshot does not check file existence or permissions. + * + * @return the destination file, or {@code null} if not applicable + */ public File destinationFile() { return destinationFile; } + /** + * Returns the bucket containing result data. + * + *
The bucket is optional and may be {@code null} when data is not yet available. No ownership + * transfer occurs; callers retain responsibility for bucket lifecycle management. + * + * @return the result data bucket, or {@code null} if not available + */ public Bucket dataBucket() { return dataBucket; } + /** + * Returns the fetch context used for the request. + * + *
The context can include MIME overrides, filter settings, and other request-scoped options. + * The snapshot stores the reference as provided and does not copy or validate it. + * + * @return the fetch context, or {@code null} if not set + */ public FetchContext fetchContext() { return fetchContext; } + /** + * Returns the scheduler priority class for the request. + * + *
The value is stored verbatim and is interpreted by the request scheduler. This snapshot does + * not enforce any valid range or normalize the value. + * + * @return the priority class as a short value + */ public short priorityClass() { return priorityClass; } + /** + * Returns the compatibility modes observed for the request. + * + *
The returned array is the original reference supplied to the constructor. It may be {@code + * null} or empty. Callers should treat it as read-only to avoid affecting equality or hash-based + * collections. + * + * @return the compatibility mode array, or {@code null} if not provided + */ public CompatibilityMode[] compatModes() { return compatModes; } + /** + * Returns the splitfile crypto key override, if any. + * + *
The returned array is not copied. It may be {@code null} when no override is configured. + * Callers should avoid mutating the array after snapshot construction. + * + * @return the splitfile key bytes, or {@code null} if no override is set + */ public byte[] splitfileKey() { return splitfileKey; } + /** + * Returns the request URI associated with this snapshot. + * + *
The URI may be {@code null} when the request has not yet resolved a definitive URI or when + * the caller chooses not to expose it. The snapshot does not validate or normalize it. + * + * @return the request URI, or {@code null} if not available + */ public FreenetURI uri() { return uri; } + /** + * Indicates whether reinsertion should skip compression. + * + *
This flag is reported to the status encoder to reflect the caller's reinsertion policy. It + * does not influence any other values held in this snapshot. + * + * @return {@code true} if reinsertion should skip compression, {@code false} otherwise + */ public boolean dontCompress() { return dontCompress; } + /** + * Compares this snapshot with another object for value equality. + * + *
Equality requires all scalar fields to match, as well as array contents for compatibility + * modes and splitfile key data. Mutable array contents can therefore affect equality over time. + * This method is deterministic provided the referenced arrays and objects are not mutated while + * comparison occurs. + * + * @param other the object to compare against, possibly {@code null} + * @return {@code true} when all fields and array contents are equal, otherwise {@code false} + */ @Override public boolean equals(Object other) { if (this == other) { @@ -187,6 +360,15 @@ public boolean equals(Object other) { && java.util.Objects.equals(uri, otherSnapshot.uri); } + /** + * Computes a hash code from all stored snapshot fields. + * + *
The hash includes the contents of the compatibility mode and splitfile key arrays, making it + * consistent with {@link #equals(Object)}. Mutating these arrays after construction can change + * the hash value and should be avoided when the instance is used as a map key. + * + * @return a hash code derived from all snapshot fields + */ @Override public int hashCode() { int result = @@ -211,6 +393,15 @@ public int hashCode() { return result; } + /** + * Returns a diagnostic string representation of this snapshot. + * + *
The string includes scalar values and full {@link Arrays#toString(byte[])} output for + * arrays. Because it may include identifiers and key material, it should be used with care in + * production logs or user-visible output. + * + * @return a human-readable representation of this snapshot + */ @Override public @NotNull String toString() { return "ClientGetStatusSnapshot[" diff --git a/src/main/java/network/crypta/clients/fcp/ClientPutDiskUploadValidator.java b/src/main/java/network/crypta/clients/fcp/ClientPutDiskUploadValidator.java index 816301f7a2a..775e37533a6 100644 --- a/src/main/java/network/crypta/clients/fcp/ClientPutDiskUploadValidator.java +++ b/src/main/java/network/crypta/clients/fcp/ClientPutDiskUploadValidator.java @@ -207,6 +207,7 @@ private static byte[] decodeFileHash(String encoded, String identifier, boolean * null} components. Downstream checks can call {@link #hasSalt()} to determine whether verification * should occur. */ +@SuppressWarnings("java:S6206") final class DiskUploadContext { private final String salt; private final byte[] saltedHash; diff --git a/src/main/java/network/crypta/clients/fcp/DownloadRequestStatusDetails.java b/src/main/java/network/crypta/clients/fcp/DownloadRequestStatusDetails.java index 5417e5c0f9d..7a834ac360c 100644 --- a/src/main/java/network/crypta/clients/fcp/DownloadRequestStatusDetails.java +++ b/src/main/java/network/crypta/clients/fcp/DownloadRequestStatusDetails.java @@ -13,6 +13,7 @@ *
This value object complements {@link RequestStatusSnapshot} by packaging fields that are * unique to {@link DownloadRequestStatus}. */ +@SuppressWarnings("java:S6206") public final class DownloadRequestStatusDetails { private final DownloadOutcomeInfo outcome; private final File destFilename; diff --git a/src/main/java/network/crypta/clients/fcp/PersistentPutRequestMetadata.java b/src/main/java/network/crypta/clients/fcp/PersistentPutRequestMetadata.java index 9f75dd7f135..48df116ec56 100644 --- a/src/main/java/network/crypta/clients/fcp/PersistentPutRequestMetadata.java +++ b/src/main/java/network/crypta/clients/fcp/PersistentPutRequestMetadata.java @@ -12,6 +12,7 @@ *
This record groups the optional private URI alongside retry and compression settings that are
* reused by both single-file and directory persistent put messages.
*/
+@SuppressWarnings("java:S6206")
public final class PersistentPutRequestMetadata {
private final FreenetURI privateURI;
private final boolean started;
diff --git a/src/main/java/network/crypta/clients/http/ConnectionsToadlet.java b/src/main/java/network/crypta/clients/http/ConnectionsToadlet.java
index 657361ba5f4..83e5e9641d5 100644
--- a/src/main/java/network/crypta/clients/http/ConnectionsToadlet.java
+++ b/src/main/java/network/crypta/clients/http/ConnectionsToadlet.java
@@ -1095,6 +1095,7 @@ private record AddPeerRequestData(
FRIEND_TRUST trust,
FRIEND_VISIBILITY visibility) {}
+ @SuppressWarnings("java:S6206")
private static final class RenderContext {
private final ToadletContext ctx;
private final String path;
diff --git a/src/main/java/network/crypta/clients/http/QueueToadlet.java b/src/main/java/network/crypta/clients/http/QueueToadlet.java
index 169d0344807..1e0bc53c6ca 100644
--- a/src/main/java/network/crypta/clients/http/QueueToadlet.java
+++ b/src/main/java/network/crypta/clients/http/QueueToadlet.java
@@ -941,6 +941,7 @@ private record DownloadTarget(String target, File downloadsDir) {}
private record BulkDownloadResult(List This record carries the algorithm selectors and metadata flag that are embedded into CHK
+ * headers and used to steer encode behavior. It is typically created alongside a {@link
+ * ClientCHKEncodePayload} and then passed into {@link ClientCHKEncodeParams} so that call sites can
+ * communicate all header-related choices in a single value. The record is a shallow, immutable
+ * carrier; it does not validate the identifiers or interpret their meaning beyond storing them.
+ *
+ * This type treats the values as simple scalars. Callers are responsible for ensuring that the
+ * identifiers match supported algorithms and that the metadata flag correctly reflects the intended
+ * key semantics. Because the record is immutable and thread-safe, it can be freely shared across
+ * threads as long as the surrounding encoding workflow is safe for reuse.
+ *
+ * This type is a small, immutable carrier for the inputs that a client-side encoder needs when
+ * producing a CHK block and its associated key material. It gathers the padded data buffer, the
+ * original unpadded length, crypto identifiers, and reusable cryptographic primitives into a single
+ * object so that call sites can pass one parameter instead of a long argument list. A typical usage
+ * is to assemble these parameters during request preparation and then hand the instance to the
+ * encoder that emits the block.
+ *
+ * The instance does not copy the provided arrays and therefore reflects the caller-provided
+ * buffers directly. Callers should treat the arrays as read-only after construction. There is no
+ * internal lifecycle beyond construction, and equality compares the complete parameter set,
+ * including array contents. This class is not thread-safe if the arrays or the {@link
+ * MessageDigest} are mutated concurrently by the caller.
+ *
+ * The returned array is the exact reference provided at construction time and is not copied.
+ * Callers should treat it as read-only to avoid surprising changes to equality or to any
+ * downstream encoding behavior. The buffer is expected to be sized for a full CHK payload, but
+ * the original length is available from {@link #dataLength()}.
+ *
+ * @return the padded payload buffer reference, not copied or normalized
+ */
public byte[] data() {
- return data;
+ return payload.data();
}
+ /**
+ * Returns the original unpadded data length in bytes.
+ *
+ * This length indicates how many bytes in the padded payload buffer are meaningful prior to
+ * padding and is typically encoded into the block header. It does not imply any validation and
+ * may be equal to the full payload size when no padding is required.
+ *
+ * @return the original unpadded data length in bytes
+ */
public int dataLength() {
- return dataLength;
+ return payload.dataLength();
}
+ /**
+ * Returns the reusable SHA-256 digest instance associated with this parameter set.
+ *
+ * The digest reference is stored as provided. If the caller reuses or mutates the digest
+ * concurrently, this instance reflects those changes and is not thread-safe by itself. This
+ * accessor performs no cloning or resetting.
+ *
+ * @return the {@link MessageDigest} instance used for SHA-256 operations
+ */
public MessageDigest md256() {
- return md256;
+ return payload.md256();
}
+ /**
+ * Returns the encryption key bytes used for block encoding.
+ *
+ * The returned array is the original reference supplied at construction time. It is not copied
+ * or protected from modification, so callers should treat it as immutable after construction to
+ * avoid inconsistent behavior across encoding and equality checks.
+ *
+ * @return the encryption key byte array reference, not copied
+ */
public byte[] encKey() {
- return encKey;
+ return payload.encKey();
}
+ /**
+ * Indicates whether the resulting key should be marked as metadata.
+ *
+ * This flag is carried into the key encoding to distinguish metadata blocks from content
+ * blocks. The value is stored verbatim and does not affect any other fields in this object.
+ *
+ * @return {@code true} when the key represents metadata, {@code false} for content
+ */
public boolean asMetadata() {
- return asMetadata;
+ return algorithms.asMetadata();
}
+ /**
+ * Returns the compression algorithm identifier stored in the key.
+ *
+ * The identifier is an implementation-defined short value used by the CHK format. This object
+ * does not validate the value; it simply stores and returns it for the encoder.
+ *
+ * @return the compression algorithm identifier as a short
+ */
public short compressionAlgorithm() {
- return compressionAlgorithm;
+ return algorithms.compressionAlgorithm();
}
+ /**
+ * Returns the crypto algorithm identifier used for block encryption.
+ *
+ * The value is stored verbatim and is intended to be encoded in the block header. This class
+ * does not interpret or validate the identifier.
+ *
+ * @return the crypto algorithm identifier as a byte
+ */
public byte cryptoAlgorithm() {
- return cryptoAlgorithm;
+ return algorithms.cryptoAlgorithm();
}
+ /**
+ * Returns the block hash algorithm identifier recorded in the header.
+ *
+ * This identifier specifies which hash algorithm should be associated with the block. The
+ * value is stored without validation or normalization.
+ *
+ * @return the block hash algorithm identifier as an integer
+ */
public int blockHashAlgorithm() {
- return blockHashAlgorithm;
+ return algorithms.blockHashAlgorithm();
}
+ /**
+ * Compares this instance with another object for value equality.
+ *
+ * Two instances are equal when all scalar fields match, and both the data and encryption key
+ * arrays contain identical contents. Reference equality compares the digest instance via {@link
+ * Objects#equals(Object, Object)}. This method is symmetric and deterministic as long as the
+ * underlying arrays and digest reference are not mutated during comparison.
+ *
+ * @param obj the object to compare against, may be {@code null}
+ * @return {@code true} when all fields and array contents match, otherwise {@code false}
+ */
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ClientCHKEncodeParams other)) return false;
- return dataLength == other.dataLength
- && asMetadata == other.asMetadata
- && compressionAlgorithm == other.compressionAlgorithm
- && cryptoAlgorithm == other.cryptoAlgorithm
- && blockHashAlgorithm == other.blockHashAlgorithm
- && Arrays.equals(data, other.data)
- && Objects.equals(md256, other.md256)
- && Arrays.equals(encKey, other.encKey);
+ return dataLength() == other.dataLength()
+ && asMetadata() == other.asMetadata()
+ && compressionAlgorithm() == other.compressionAlgorithm()
+ && cryptoAlgorithm() == other.cryptoAlgorithm()
+ && blockHashAlgorithm() == other.blockHashAlgorithm()
+ && Arrays.equals(data(), other.data())
+ && Objects.equals(md256(), other.md256())
+ && Arrays.equals(encKey(), other.encKey());
}
+ /**
+ * Computes a hash code based on all stored parameters.
+ *
+ * The hash combines scalar fields and array contents so it is consistent with {@link
+ * #equals(Object)}. If the arrays are mutated after construction, the hash code may change and
+ * the instance should not be used as a stable key in hash-based collections.
+ *
+ * @return a hash code derived from all stored fields and array contents
+ */
@Override
public int hashCode() {
int result =
Objects.hash(
- md256,
- dataLength,
- asMetadata,
- compressionAlgorithm,
- cryptoAlgorithm,
- blockHashAlgorithm);
- result = 31 * result + Arrays.hashCode(data);
- result = 31 * result + Arrays.hashCode(encKey);
+ md256(),
+ dataLength(),
+ asMetadata(),
+ compressionAlgorithm(),
+ cryptoAlgorithm(),
+ blockHashAlgorithm());
+ result = 31 * result + Arrays.hashCode(data());
+ result = 31 * result + Arrays.hashCode(encKey());
return result;
}
+ /**
+ * Returns a string representation of this parameter set.
+ *
+ * The string includes the scalar values and a full {@link Arrays#toString(byte[])} rendering
+ * of the data and encryption key arrays. This is intended for diagnostics and should not be used
+ * for logging sensitive material in production contexts.
+ *
+ * @return a human-readable representation of this parameter set
+ */
@Override
public @NotNull String toString() {
return "ClientCHKEncodeParams["
+ "data="
- + Arrays.toString(data)
+ + Arrays.toString(data())
+ ", dataLength="
- + dataLength
+ + dataLength()
+ ", md256="
- + md256
+ + md256()
+ ", encKey="
- + Arrays.toString(encKey)
+ + Arrays.toString(encKey())
+ ", asMetadata="
- + asMetadata
+ + asMetadata()
+ ", compressionAlgorithm="
- + compressionAlgorithm
+ + compressionAlgorithm()
+ ", cryptoAlgorithm="
- + cryptoAlgorithm
+ + cryptoAlgorithm()
+ ", blockHashAlgorithm="
- + blockHashAlgorithm
+ + blockHashAlgorithm()
+ "]";
}
}
diff --git a/src/main/java/network/crypta/keys/ClientCHKEncodePayload.java b/src/main/java/network/crypta/keys/ClientCHKEncodePayload.java
new file mode 100644
index 00000000000..2c879af654c
--- /dev/null
+++ b/src/main/java/network/crypta/keys/ClientCHKEncodePayload.java
@@ -0,0 +1,176 @@
+package network.crypta.keys;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Bundles the payload inputs required for client-side CHK encoding.
+ *
+ * This value object captures the data buffer, the original unpadded length, a caller-supplied
+ * SHA-256 {@link MessageDigest}, and the encryption key bytes used during CHK block creation. It is
+ * typically assembled after compression/padding and before encryption so that encode paths can pass
+ * a single object rather than multiple parallel arguments. Instances are shallow and immutable with
+ * respect to the field references, but the referenced arrays and digest remain caller-owned and
+ * mutable.
+ *
+ * The payload does not validate sizes or algorithm choices; it is a transport container whose
+ * consumers enforce preconditions. Because the arrays are not copied, callers should treat them as
+ * read-only for the duration of encoding to avoid inconsistent results in equality, hash codes, or
+ * downstream cryptographic outputs. The instance has no lifecycle beyond construction and may be
+ * safely reused by sequential operations when the referenced objects are stable.
+ *
+ * This constructor stores the provided references verbatim and performs no validation or
+ * defensive copying. The {@code data} buffer is expected to represent a padded CHK payload (often
+ * {@link CHKBlock#DATA_LENGTH} bytes), while {@code dataLength} records the original unpadded
+ * size used in header computations. The digest and key are retained for downstream use, and
+ * callers remain responsible for their lifecycle and mutability.
+ *
+ * @param data padded payload buffer reference; treated as read-only by callers during encoding
+ * @param dataLength original unpadded data length in bytes; typically within payload size bounds
+ * @param md256 reusable SHA-256 digest instance; may be shared and is not cloned by this object
+ * @param encKey encryption key bytes for the block; stored as provided without copying
+ */
+ public ClientCHKEncodePayload(byte[] data, int dataLength, MessageDigest md256, byte[] encKey) {
+ this.data = data;
+ this.dataLength = dataLength;
+ this.md256 = md256;
+ this.encKey = encKey;
+ }
+
+ /**
+ * Returns the padded payload buffer reference used for encoding.
+ *
+ * The returned array is the exact reference supplied at construction time and is not copied.
+ * Callers should treat it as immutable for the duration of encoding to avoid non-deterministic
+ * cryptographic output or inconsistent equality comparisons. The buffer is expected to contain
+ * any padding already applied, with the original length accessible via {@link #dataLength()}.
+ *
+ * @return the padded payload byte array reference, not copied or normalized
+ */
+ public byte[] data() {
+ return data;
+ }
+
+ /**
+ * Returns the original unpadded data length in bytes.
+ *
+ * This value records how many bytes in the padded payload are meaningful prior to padding and
+ * is typically encoded into the CHK header. The value is stored verbatim without range checks, so
+ * callers should ensure it is consistent with the associated buffer size and encoding rules.
+ *
+ * @return the original unpadded length in bytes, as supplied at construction time
+ */
+ public int dataLength() {
+ return dataLength;
+ }
+
+ /**
+ * Returns the SHA-256 {@link MessageDigest} reference associated with this payload.
+ *
+ * The digest instance is stored as provided and is neither cloned nor reset. Callers are
+ * responsible for any synchronization if the digest is reused concurrently and for resetting its
+ * state if they share it across operations. This method returns the same reference each time.
+ *
+ * @return the caller-managed digest instance used for SHA-256 operations
+ */
+ public MessageDigest md256() {
+ return md256;
+ }
+
+ /**
+ * Returns the encryption key bytes used for the block.
+ *
+ * The returned array is the original reference supplied at construction time. It is not copied
+ * or protected from modification, so callers should treat it as immutable for the duration of any
+ * encoding that relies on deterministic key material. The interpretation of the bytes depends on
+ * the selected crypto algorithm in the surrounding encoding parameters.
+ *
+ * @return the encryption key byte array reference, not copied or sanitized
+ */
+ public byte[] encKey() {
+ return encKey;
+ }
+
+ /**
+ * Compares this payload with another object for value equality.
+ *
+ * Two payloads are equal when their {@code dataLength} values match, the {@code data} and
+ * {@code encKey} arrays contain identical contents, and the digest references are equal according
+ * to {@link Objects#equals(Object, Object)}. This comparison is deterministic only if the
+ * underlying arrays and digest reference are not mutated during comparison.
+ *
+ * @param obj object to compare against; may be {@code null}
+ * @return {@code true} when all stored fields and array contents are equal
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof ClientCHKEncodePayload other)) return false;
+ return dataLength == other.dataLength
+ && Arrays.equals(data, other.data)
+ && Objects.equals(md256, other.md256)
+ && Arrays.equals(encKey, other.encKey);
+ }
+
+ /**
+ * Computes a hash code consistent with {@link #equals(Object)}.
+ *
+ * The hash mixes the digest reference, the scalar length, and the array contents. Because the
+ * arrays are mutable and not copied, the hash code is only stable when the referenced arrays and
+ * digest remain unchanged. Avoid using instances as long-lived keys in hash-based collections
+ * when the underlying buffers may be mutated.
+ *
+ * @return a hash code derived from the digest reference, length, and array contents
+ */
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(md256, dataLength);
+ result = 31 * result + Arrays.hashCode(data);
+ result = 31 * result + Arrays.hashCode(encKey);
+ return result;
+ }
+
+ /**
+ * Returns a diagnostic string describing the payload contents.
+ *
+ * The representation includes the full array contents via {@link Arrays#toString(byte[])} and
+ * the digest reference. This can be useful for debugging but may expose sensitive material or be
+ * expensive for large buffers. Callers should avoid logging the result in production contexts
+ * where key material or payload bytes must remain confidential.
+ *
+ * @return a non-null string containing all stored fields and array contents
+ */
+ @Override
+ public @NotNull String toString() {
+ return "ClientCHKEncodePayload[data="
+ + Arrays.toString(data)
+ + ", dataLength="
+ + dataLength
+ + ", md256="
+ + md256
+ + ", encKey="
+ + Arrays.toString(encKey)
+ + "]";
+ }
+}
diff --git a/src/main/java/network/crypta/keys/FreenetURI.java b/src/main/java/network/crypta/keys/FreenetURI.java
index e6e62ff9715..9767fbab103 100644
--- a/src/main/java/network/crypta/keys/FreenetURI.java
+++ b/src/main/java/network/crypta/keys/FreenetURI.java
@@ -406,6 +406,7 @@ private static String validateKeyTypeOrThrow(String kt) throws MalformedURLExcep
throw new MalformedURLException("Invalid key type: " + kt);
}
+ @SuppressWarnings("java:S6206")
private static final class MetaParse {
private final String docName;
private final String[] meta;
@@ -535,6 +536,7 @@ private static String[] toMetaArray(List
+ *
+ *
+ * @param asMetadata whether the resulting key represents metadata rather than content
+ * @param compressionAlgorithm compression algorithm identifier recorded in the generated key
+ * @param cryptoAlgorithm crypto algorithm identifier indicating the cipher used for the block
+ * @param blockHashAlgorithm block hash identifier recorded in the block header
+ * @see ClientCHKEncodeParams
+ * @see ClientCHKEncodePayload
+ */
+public record ClientCHKEncodeAlgorithms(
+ boolean asMetadata, short compressionAlgorithm, byte cryptoAlgorithm, int blockHashAlgorithm) {}
diff --git a/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java b/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java
index fb27e0340c9..66bd07a30c6 100644
--- a/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java
+++ b/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java
@@ -5,128 +5,224 @@
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
-/** Bundles the parameters needed to build a client-side CHK block. */
+/**
+ * Bundles the parameters needed to build a client-side CHK block.
+ *
+ *
+ *
+ *
+ * @see CHKBlock
+ */
public final class ClientCHKEncodeParams {
- private final byte[] data;
- private final int dataLength;
- private final MessageDigest md256;
- private final byte[] encKey;
- private final boolean asMetadata;
- private final short compressionAlgorithm;
- private final byte cryptoAlgorithm;
- private final int blockHashAlgorithm;
+ private final ClientCHKEncodePayload payload;
+ private final ClientCHKEncodeAlgorithms algorithms;
/**
* Creates a parameter bundle for CHK block encoding.
*
- * @param data padded data (exactly {@link CHKBlock#DATA_LENGTH} bytes)
- * @param dataLength original, unpadded length in bytes
- * @param md256 reusable SHA-256 instance
- * @param encKey encryption key to use
- * @param asMetadata whether the resulting key is metadata
- * @param compressionAlgorithm compression algorithm identifier stored in the key
- * @param cryptoAlgorithm crypto algorithm identifier
- * @param blockHashAlgorithm block-hash identifier to store in the header
+ * @param payload payload buffers, lengths, and digest to use for encoding
+ * @param algorithms metadata and algorithm identifiers to embed in the block header
*/
public ClientCHKEncodeParams(
- byte[] data,
- int dataLength,
- MessageDigest md256,
- byte[] encKey,
- boolean asMetadata,
- short compressionAlgorithm,
- byte cryptoAlgorithm,
- int blockHashAlgorithm) {
- this.data = data;
- this.dataLength = dataLength;
- this.md256 = md256;
- this.encKey = encKey;
- this.asMetadata = asMetadata;
- this.compressionAlgorithm = compressionAlgorithm;
- this.cryptoAlgorithm = cryptoAlgorithm;
- this.blockHashAlgorithm = blockHashAlgorithm;
+ ClientCHKEncodePayload payload, ClientCHKEncodeAlgorithms algorithms) {
+ this.payload = payload;
+ this.algorithms = algorithms;
}
+ /**
+ * Returns the padded data buffer used for block encoding.
+ *
+ *
+ *
+ *
+ * @see ClientCHKEncodeParams
+ * @see ClientCHKEncodeAlgorithms
+ */
+@SuppressWarnings({"java:S6206", "ClassCanBeRecord"})
+public final class ClientCHKEncodePayload {
+ private final byte[] data;
+ private final int dataLength;
+ private final MessageDigest md256;
+ private final byte[] encKey;
+
+ /**
+ * Creates a payload bundle for CHK block encoding.
+ *
+ *