ZeroEchoSigner.java

1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright 2026 Egothor
3
// Copyright 2026 Accenture
4
package org.egothor.methodatlas.evidence;
5
6
import java.io.IOException;
7
import java.io.InputStream;
8
import java.io.OutputStream;
9
import java.nio.file.Files;
10
import java.nio.file.Path;
11
import java.security.GeneralSecurityException;
12
import java.security.PrivateKey;
13
import java.security.Signature;
14
import java.util.List;
15
import java.util.Locale;
16
import java.util.function.Supplier;
17
import java.util.logging.Level;
18
import java.util.logging.Logger;
19
20
import zeroecho.core.context.SignatureContext;
21
import zeroecho.core.spec.VoidSpec;
22
import zeroecho.core.storage.KeyringStore;
23
import zeroecho.core.tag.TagEngine;
24
import zeroecho.core.tag.TagEngineBuilder;
25
import zeroecho.sdk.builders.TagTrailerDataContentBuilder;
26
import zeroecho.sdk.content.api.DataContent;
27
import zeroecho.sdk.content.builtin.PlainFile;
28
import zeroecho.sdk.hybrid.signature.HybridSignatureContexts;
29
import zeroecho.sdk.hybrid.signature.HybridSignatureProfile;
30
import zeroecho.sdk.util.BouncyCastleActivator;
31
32
/**
33
 * Produces a signed envelope of the evidence-pack manifest using the ZeroEcho
34
 * cryptographic toolkit (version 1.1.1).
35
 *
36
 * <p>
37
 * The signing key is read from a ZeroEcho {@link KeyringStore} — a plaintext
38
 * UTF-8 keyring file, <em>not</em> a JDK PKCS12/JKS keystore and not anything
39
 * produced by {@code keytool}. The keyring is loaded with
40
 * {@link KeyringStore#load(Path)} and the private key is resolved by alias.
41
 * Generate a keyring with MethodAtlas's {@code -gen-signing-key} mode (see
42
 * {@link SigningKeyGenerator}) or with ZeroEcho's own {@code -K --generate}
43
 * command-line tool.
44
 * </p>
45
 *
46
 * <h2>Single-algorithm signing</h2>
47
 * <p>
48
 * When the signature algorithm contains no {@value #HYBRID_SEP} separator the
49
 * manifest is signed with one algorithm. The algorithm defaults to
50
 * {@value #DEFAULT_ALGO}; when no explicit algorithm is supplied the value
51
 * stored alongside the key in the keyring is used. RSA keys are signed with
52
 * RSA-PSS (SHA-256) and ECDSA keys with the P-256 curve; every other algorithm
53
 * (Ed25519, Ed448, SPHINCS+, ML-DSA, SLH-DSA, …) is dispatched through
54
 * {@link TagEngineBuilder#signature(String, java.security.Key, zeroecho.core.spec.ContextSpec)}.
55
 * </p>
56
 *
57
 * <h2>Hybrid signing</h2>
58
 * <p>
59
 * A signature algorithm of the form {@code classic+pqc} (for example
60
 * {@code Ed25519+SPHINCS+}) selects a hybrid composite that signs with both a
61
 * classical and a post-quantum primitive. The key alias must then carry both
62
 * component aliases separated by {@value #ALIAS_SEP}
63
 * ({@code classicAlias/pqcAlias}). Verification of a hybrid envelope requires
64
 * both component signatures (AND rule).
65
 * </p>
66
 *
67
 * <h2>Thread-safety and lifecycle</h2>
68
 * <p>
69
 * Instances are <strong>not</strong> thread-safe and are single-use: create one
70
 * signer per {@link #sign(Path, Path)} call. Hybrid signers in particular hold a
71
 * stateful {@link SignatureContext} that must not be reused across streams. The
72
 * signer is {@link AutoCloseable}; callers should use it in a
73
 * try-with-resources block (or call {@link #close()}) so the owned hybrid
74
 * context is released — close it even if {@link #sign(Path, Path)} is never
75
 * invoked.
76
 * </p>
77
 *
78
 * <p>
79
 * Package-private because only {@link EvidencePackCommand} composes this signer.
80
 * </p>
81
 *
82
 * @see SigningKeyGenerator
83
 * @see <a href="https://gitea.egothor.org/Egothor/ZeroEcho">ZeroEcho</a>
84
 * @since 4.0.0
85
 */
86
final class ZeroEchoSigner implements AutoCloseable {
87
88
    private static final Logger LOG = Logger.getLogger(ZeroEchoSigner.class.getName());
89
90
    /** Stream buffer size used when copying through the signing pipeline. */
91
    /* default */ static final int BUFFER_SIZE = 8192;
92
93
    /**
94
     * Upper bound on the manifest body buffered by the hybrid DoS guard — 16 MiB
95
     * is far more than any realistic SHA-256 manifest.
96
     */
97
    /* default */ static final int MAX_BODY_BYTES = 16 * 1024 * 1024;
98
99
    /** Default single-algorithm identifier when none is supplied and none is stored. */
100
    /* default */ static final String DEFAULT_ALGO = "Ed25519";
101
102
    /** Separator that distinguishes hybrid algorithm specifications ({@code classic+pqc}). */
103
    /* default */ static final String HYBRID_SEP = "+";
104
105
    /** Separator used inside the key-alias parameter to split classical and PQC aliases. */
106
    /* default */ static final String ALIAS_SEP = "/";
107
108
    /** Version of the ZeroEcho library recorded in {@code pack-meta.json}. */
109
    /* default */ static final String ZEROECHO_LIB_VERSION = "1.1.1";
110
111
    /** Factory that supplies a fresh signing engine for the manifest pipeline. */
112
    private final Supplier<TagEngine<Signature>> engineFactory;
113
114
    /** Resolved algorithm string, reported by callers in pack metadata. */
115
    private final String algorithm;
116
117
    /** Effective keystore alias actually used to load the signing key. */
118
    private final String resolvedAlias;
119
120
    /**
121
     * Closeable cryptographic context owned by this signer, or {@code null}. The
122
     * hybrid path eagerly creates a {@link SignatureContext} that this signer
123
     * retains so {@link #close()} can release it; the single-algorithm path has
124
     * no owned context (its per-stream engines are created and owned by the
125
     * signing pipeline).
126
     */
127
    private final SignatureContext ownedContext;
128
129
    /**
130
     * Private constructor; instances are obtained through
131
     * {@link #fromKeyringFile(Path, String, String)} or
132
     * {@link #fromKeyringText(String, String, String)}.
133
     *
134
     * @param engineFactory supplier of the signing engine; must not be {@code null}
135
     * @param algorithm     algorithm string (e.g. {@code "Ed25519"})
136
     * @param resolvedAlias keyring alias actually used
137
     * @param ownedContext  closeable context this signer owns, or {@code null}
138
     *                      when there is none to release
139
     */
140
    private ZeroEchoSigner(Supplier<TagEngine<Signature>> engineFactory, String algorithm, String resolvedAlias,
141
            SignatureContext ownedContext) {
142
        this.engineFactory = engineFactory;
143
        this.algorithm = algorithm;
144
        this.resolvedAlias = resolvedAlias;
145
        this.ownedContext = ownedContext;
146
    }
147
148
    /**
149
     * Releases the cryptographic context this signer owns. A no-op for the
150
     * single-algorithm path; for hybrid signers it closes the retained
151
     * {@link SignatureContext}. Failures to close are logged at {@code FINE} and
152
     * swallowed, since {@code close()} is best-effort cleanup and must not mask a
153
     * signing result.
154
     */
155
    @Override
156
    public void close() {
157 2 1. close : removed conditional - replaced equality check with false → KILLED
2. close : removed conditional - replaced equality check with true → KILLED
        if (ownedContext != null) {
158
            try {
159 1 1. close : removed call to zeroecho/core/context/SignatureContext::close → KILLED
                ownedContext.close();
160
            } catch (IOException e) {
161
                if (LOG.isLoggable(Level.FINE)) {
162
                    LOG.log(Level.FINE, "Failed to close hybrid signature context", e);
163
                }
164
            }
165
        }
166
    }
167
168
    /**
169
     * Returns the algorithm string that will be reported in pack metadata.
170
     *
171
     * @return algorithm identifier; never {@code null}
172
     */
173
    /* default */ String algorithm() {
174 1 1. algorithm : replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::algorithm → KILLED
        return algorithm;
175
    }
176
177
    /**
178
     * Returns the keyring alias actually used to retrieve the signing key.
179
     *
180
     * @return alias; never {@code null} once construction succeeded
181
     */
182
    /* default */ String resolvedAlias() {
183 1 1. resolvedAlias : replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::resolvedAlias → KILLED
        return resolvedAlias;
184
    }
185
186
    /**
187
     * Creates a signer from a ZeroEcho keyring file on disk. This is the path
188
     * used for interactive CLI signing, where the keyring file is protected by
189
     * file-system permissions or ACLs.
190
     *
191
     * @param keyringFile        path to the ZeroEcho keyring; must not be {@code null}
192
     * @param keyAlias           alias to load; {@code null} means use the first
193
     *                           alias in the keyring; for hybrid the format is
194
     *                           {@code classicAlias/pqcAlias}
195
     * @param signatureAlgorithm algorithm identifier; {@code null} means derive it
196
     *                           from the keyring entry (single-algorithm only); a
197
     *                           value of the form {@code classic+pqc} selects
198
     *                           hybrid signing
199
     * @return signer ready to sign; never {@code null}
200
     * @throws IOException              if reading the keyring or building the
201
     *                                  signing context fails
202
     * @throws GeneralSecurityException if a key cannot be materialised
203
     */
204
    /* default */ static ZeroEchoSigner fromKeyringFile(Path keyringFile, String keyAlias,
205
            String signatureAlgorithm) throws IOException, GeneralSecurityException {
206 1 1. fromKeyringFile : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::fromKeyringFile → KILLED
        return build(KeyringStore.load(keyringFile), keyAlias, signatureAlgorithm);
207
    }
208
209
    /**
210
     * Creates a signer from in-memory keyring text. This is the path used in
211
     * CI/CD pipelines, where the keyring content is delivered through a platform
212
     * secret (an environment variable) and is parsed in memory so the private
213
     * key never touches the runner's disk.
214
     *
215
     * @param keyringText        full keyring content (including the
216
     *                           {@code # KeyringStore v1} header), typically the
217
     *                           value of a CI secret variable; must not be
218
     *                           {@code null} or blank
219
     * @param keyAlias           alias to load; {@code null} means use the first
220
     *                           alias in the keyring; for hybrid the format is
221
     *                           {@code classicAlias/pqcAlias}
222
     * @param signatureAlgorithm algorithm identifier; {@code null} means derive it
223
     *                           from the keyring entry; a value of the form
224
     *                           {@code classic+pqc} selects hybrid signing
225
     * @return signer ready to sign; never {@code null}
226
     * @throws IOException              if the keyring text is malformed or building
227
     *                                  the signing context fails
228
     * @throws GeneralSecurityException if a key cannot be materialised
229
     */
230
    /* default */ static ZeroEchoSigner fromKeyringText(String keyringText, String keyAlias,
231
            String signatureAlgorithm) throws IOException, GeneralSecurityException {
232 4 1. fromKeyringText : removed conditional - replaced equality check with true → SURVIVED
2. fromKeyringText : removed conditional - replaced equality check with false → SURVIVED
3. fromKeyringText : removed conditional - replaced equality check with true → KILLED
4. fromKeyringText : removed conditional - replaced equality check with false → KILLED
        if (keyringText == null || keyringText.isBlank()) {
233
            throw new IOException("Keyring content is empty");
234
        }
235
        KeyringStore keyring = new KeyringStore();
236 1 1. fromKeyringText : removed call to zeroecho/core/storage/KeyringStore::importText → KILLED
        keyring.importText(keyringText, true);
237 1 1. fromKeyringText : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::fromKeyringText → KILLED
        return build(keyring, keyAlias, signatureAlgorithm);
238
    }
239
240
    /**
241
     * Builds a signer from an already-loaded keyring, dispatching to the single
242
     * or hybrid construction path based on {@code signatureAlgorithm}.
243
     *
244
     * @param keyring            loaded keyring store; must not be {@code null}
245
     * @param keyAlias           alias to load, or {@code null} for the first alias
246
     * @param signatureAlgorithm algorithm identifier, or {@code null} to derive it
247
     * @return configured signer
248
     * @throws IOException              if the alias/algorithm pairing is invalid
249
     * @throws GeneralSecurityException if a key cannot be materialised
250
     */
251
    private static ZeroEchoSigner build(KeyringStore keyring, String keyAlias, String signatureAlgorithm)
252
            throws IOException, GeneralSecurityException {
253
        // SPHINCS+ and the other PQC primitives are provided by Bouncy Castle;
254
        // init is idempotent and harmless for classical algorithms.
255 1 1. build : removed call to zeroecho/sdk/util/BouncyCastleActivator::init → SURVIVED
        BouncyCastleActivator.init();
256 4 1. build : removed conditional - replaced equality check with false → KILLED
2. build : removed conditional - replaced equality check with false → KILLED
3. build : removed conditional - replaced equality check with true → KILLED
4. build : removed conditional - replaced equality check with true → KILLED
        if (signatureAlgorithm != null && signatureAlgorithm.contains(HYBRID_SEP)) {
257 1 1. build : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::build → KILLED
            return buildHybrid(keyring, keyAlias, signatureAlgorithm);
258
        }
259 1 1. build : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::build → KILLED
        return buildSingle(keyring, keyAlias, signatureAlgorithm);
260
    }
261
262
    /**
263
     * Signs {@code inputFile} and writes the resulting signed envelope to
264
     * {@code outputFile}. The output contains the original bytes followed by a
265
     * ZeroEcho signature trailer.
266
     *
267
     * @param inputFile  manifest to sign
268
     * @param outputFile destination for the signed envelope
269
     * @return {@code outputFile} on success
270
     * @throws IOException              if reading, signing, or writing fails
271
     * @throws GeneralSecurityException if the cryptographic step fails
272
     */
273
    // ZeroEcho's builders surface configuration failures as unchecked exceptions
274
    // (IllegalStateException from the engine factory, IllegalArgumentException for
275
    // bad parameters); translating them to IOException keeps the CLI from crashing
276
    // mid-pack with a raw stack trace.
277
    @SuppressWarnings("PMD.AvoidCatchingGenericException")
278
    /* default */ Path sign(Path inputFile, Path outputFile) throws IOException, GeneralSecurityException {
279
        try {
280
            DataContent tail = new TagTrailerDataContentBuilder<>(engineFactory)
281
                    .bufferSize(BUFFER_SIZE)
282
                    .build(true);
283 1 1. sign : removed call to zeroecho/sdk/content/api/DataContent::setInput → KILLED
            tail.setInput(new PlainFile(inputFile.toUri().toURL()));
284
            try (InputStream in = tail.getStream();
285
                    OutputStream out = Files.newOutputStream(outputFile)) {
286
                in.transferTo(out);
287
            }
288 1 1. sign : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::sign → KILLED
            return outputFile;
289
        } catch (RuntimeException e) {
290
            throw new IOException("Failed to sign manifest with ZeroEcho (" + algorithm + ")", e);
291
        }
292
    }
293
294
    // -------------------------------------------------------------------------
295
    // Construction helpers
296
    // -------------------------------------------------------------------------
297
298
    /**
299
     * Builds a single-algorithm signer, deriving the algorithm from the keyring
300
     * entry when {@code explicitAlgorithm} is {@code null}.
301
     *
302
     * @param keyring           loaded keyring store
303
     * @param keyAlias          requested alias, or {@code null} for the first alias
304
     * @param explicitAlgorithm caller-supplied algorithm, or {@code null} to derive it
305
     * @return configured signer
306
     * @throws IOException              if the keyring is empty
307
     * @throws GeneralSecurityException if the private key cannot be materialised
308
     */
309
    private static ZeroEchoSigner buildSingle(KeyringStore keyring, String keyAlias, String explicitAlgorithm)
310
            throws IOException, GeneralSecurityException {
311
        String alias = resolveAlias(keyring, keyAlias);
312
        KeyringStore.PrivateWithId entry = keyring.getPrivateWithId(alias);
313 2 1. buildSingle : removed conditional - replaced equality check with true → KILLED
2. buildSingle : removed conditional - replaced equality check with false → KILLED
        String algo = explicitAlgorithm != null ? explicitAlgorithm : entry.algorithm();
314
        Supplier<TagEngine<Signature>> factory = engineFactoryFor(algo, entry.key());
315 1 1. buildSingle : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::buildSingle → KILLED
        return new ZeroEchoSigner(factory, algo, alias, null);
316
    }
317
318
    /**
319
     * Builds a hybrid (classical + post-quantum) signer.
320
     *
321
     * @param keyring  loaded keyring store
322
     * @param keyAlias combined alias {@code classicAlias/pqcAlias}; must not be {@code null}
323
     * @param algo     combined algorithm {@code classic+pqc}
324
     * @return configured signer
325
     * @throws IOException              if the alias is not a valid hybrid pair
326
     * @throws GeneralSecurityException if either private key cannot be materialised
327
     */
328
    private static ZeroEchoSigner buildHybrid(KeyringStore keyring, String keyAlias, String algo)
329
            throws IOException, GeneralSecurityException {
330
        String[] algoParts = splitPair(algo, HYBRID_SEP,
331
                "Hybrid algorithm must be 'classic+pqc' (e.g. Ed25519+SPHINCS+): " + algo);
332 4 1. buildHybrid : removed conditional - replaced equality check with true → SURVIVED
2. buildHybrid : removed conditional - replaced equality check with false → TIMED_OUT
3. buildHybrid : removed conditional - replaced equality check with false → KILLED
4. buildHybrid : removed conditional - replaced equality check with true → KILLED
        if (keyAlias == null || !keyAlias.contains(ALIAS_SEP)) {
333
            throw new IOException("Hybrid signing requires a 'classicAlias/pqcAlias' key alias via "
334
                    + "-evidence-pack-key-alias; got: " + keyAlias);
335
        }
336
        String[] aliasParts = splitPair(keyAlias, ALIAS_SEP,
337
                "Hybrid key alias must be 'classicAlias/pqcAlias': " + keyAlias);
338
339
        PrivateKey classicKey = keyring.getPrivate(aliasParts[0]);
340
        PrivateKey pqcKey = keyring.getPrivate(aliasParts[1]);
341
342
        HybridSignatureProfile profile = new HybridSignatureProfile(
343
                algoParts[0], algoParts[1], null, null, HybridSignatureProfile.VerifyRule.AND);
344
        SignatureContext context = HybridSignatureContexts.sign(profile, classicKey, pqcKey, MAX_BODY_BYTES);
345
        // The hybrid context is stateful and single-use; the signer is documented as
346
        // single-use, so reusing the same instance across the one sign() call is safe.
347
        // The signer retains the context and releases it in close().
348 2 1. buildHybrid : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::buildHybrid → KILLED
2. lambda$buildHybrid$0 : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::lambda$buildHybrid$0 → KILLED
        return new ZeroEchoSigner(() -> context, algo, keyAlias, context);
349
    }
350
351
    /**
352
     * Resolves the per-algorithm ZeroEcho engine factory. RSA keys use RSA-PSS
353
     * (SHA-256, 32-byte salt) and ECDSA keys use the P-256 curve, matching the
354
     * dedicated {@link TagEngineBuilder} factories; every other identifier is
355
     * dispatched through the generic
356
     * {@link TagEngineBuilder#signature(String, java.security.Key, zeroecho.core.spec.ContextSpec)}
357
     * factory, which covers Ed25519, Ed448, SPHINCS+, ML-DSA, and SLH-DSA.
358
     *
359
     * @param algo       canonical algorithm string
360
     * @param privateKey loaded private key
361
     * @return supplier producing fresh signing engines
362
     */
363
    private static Supplier<TagEngine<Signature>> engineFactoryFor(String algo, PrivateKey privateKey) {
364
        String normalised = algo.toUpperCase(Locale.ROOT);
365 2 1. engineFactoryFor : Changed switch default to be first case → KILLED
2. engineFactoryFor : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::engineFactoryFor → KILLED
        return switch (normalised) {
366
            case "RSA", "RSA-PSS", "RSASSA-PSS" -> TagEngineBuilder.rsaSign(privateKey, null);
367
            case "ECDSA" -> TagEngineBuilder.ecdsaSign(privateKey, null);
368
            default -> TagEngineBuilder.signature(algo, privateKey, VoidSpec.INSTANCE);
369
        };
370
    }
371
372
    /**
373
     * Returns the alias to use: {@code keyAlias} when non-null, otherwise the
374
     * first alias in the keyring.
375
     *
376
     * @param keyring  keyring to query
377
     * @param keyAlias requested alias, or {@code null}
378
     * @return resolved alias
379
     * @throws IOException if the keyring contains no aliases
380
     */
381
    private static String resolveAlias(KeyringStore keyring, String keyAlias) throws IOException {
382 2 1. resolveAlias : removed conditional - replaced equality check with false → SURVIVED
2. resolveAlias : removed conditional - replaced equality check with true → KILLED
        if (keyAlias != null) {
383 1 1. resolveAlias : replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::resolveAlias → KILLED
            return keyAlias;
384
        }
385
        List<String> aliases = keyring.aliases();
386 2 1. resolveAlias : removed conditional - replaced equality check with false → SURVIVED
2. resolveAlias : removed conditional - replaced equality check with true → KILLED
        if (aliases.isEmpty()) {
387
            throw new IOException("Keyring contains no aliases");
388
        }
389 1 1. resolveAlias : replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::resolveAlias → KILLED
        return aliases.get(0);
390
    }
391
392
    /**
393
     * Splits {@code value} into exactly two non-blank halves around {@code sep}.
394
     *
395
     * @param value   value to split
396
     * @param sep     literal separator
397
     * @param message error message used when the split does not yield two halves
398
     * @return a two-element array of trimmed, non-blank parts
399
     * @throws IOException if {@code value} does not split into two non-blank parts
400
     */
401
    private static String[] splitPair(String value, String sep, String message) throws IOException {
402
        int idx = value.indexOf(sep);
403 7 1. splitPair : changed conditional boundary → SURVIVED
2. splitPair : removed conditional - replaced comparison check with false → TIMED_OUT
3. splitPair : changed conditional boundary → TIMED_OUT
4. splitPair : Replaced integer subtraction with addition → TIMED_OUT
5. splitPair : removed conditional - replaced comparison check with true → TIMED_OUT
6. splitPair : removed conditional - replaced comparison check with false → KILLED
7. splitPair : removed conditional - replaced comparison check with true → KILLED
        if (idx <= 0 || idx >= value.length() - sep.length()) {
404
            throw new IOException(message);
405
        }
406
        String first = value.substring(0, idx).trim();
407 1 1. splitPair : Replaced integer addition with subtraction → KILLED
        String second = value.substring(idx + sep.length()).trim();
408 4 1. splitPair : removed conditional - replaced equality check with false → TIMED_OUT
2. splitPair : removed conditional - replaced equality check with true → TIMED_OUT
3. splitPair : removed conditional - replaced equality check with false → KILLED
4. splitPair : removed conditional - replaced equality check with true → KILLED
        if (first.isEmpty() || second.isEmpty()) {
409
            throw new IOException(message);
410
        }
411 1 1. splitPair : replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::splitPair → KILLED
        return new String[] { first, second };
412
    }
413
}

Mutations

157

1.1
Location : close
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : close
Killed by : org.egothor.methodatlas.EvidencePackCommandTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.EvidencePackCommandTest]/[method:signsManifestWithGeneratedEd25519Keyring(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

159

1.1
Location : close
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
removed call to zeroecho/core/context/SignatureContext::close → KILLED

174

1.1
Location : algorithm
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:derivesAlgorithmFromKeyringWhenUnspecified()]
replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::algorithm → KILLED

183

1.1
Location : resolvedAlias
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::resolvedAlias → KILLED

206

1.1
Location : fromKeyringFile
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::fromKeyringFile → KILLED

232

1.1
Location : fromKeyringText
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:signsFromInMemoryKeyringText()]
removed conditional - replaced equality check with true → KILLED

2.2
Location : fromKeyringText
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:signsFromInMemoryKeyringText()]
removed conditional - replaced equality check with false → KILLED

3.3
Location : fromKeyringText
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

4.4
Location : fromKeyringText
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

236

1.1
Location : fromKeyringText
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:signsFromInMemoryKeyringText()]
removed call to zeroecho/core/storage/KeyringStore::importText → KILLED

237

1.1
Location : fromKeyringText
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:signsFromInMemoryKeyringText()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::fromKeyringText → KILLED

255

1.1
Location : build
Killed by : none
removed call to zeroecho/sdk/util/BouncyCastleActivator::init → SURVIVED
Covering tests

256

1.1
Location : build
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:hybridSigningRejectsAliasWithoutSeparator()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : build
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:hybridSigningRejectsAliasWithoutSeparator()]
removed conditional - replaced equality check with false → KILLED

3.3
Location : build
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:verificationFailsWhenSignedEnvelopeIsTampered()]
removed conditional - replaced equality check with true → KILLED

4.4
Location : build
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
removed conditional - replaced equality check with true → KILLED

257

1.1
Location : build
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::build → KILLED

259

1.1
Location : build
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::build → KILLED

283

1.1
Location : sign
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:verificationFailsWhenSignedEnvelopeIsTampered()]
removed call to zeroecho/sdk/content/api/DataContent::setInput → KILLED

288

1.1
Location : sign
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:verificationFailsWhenSignedEnvelopeIsTampered()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::sign → KILLED

313

1.1
Location : buildSingle
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
removed conditional - replaced equality check with true → KILLED

2.2
Location : buildSingle
Killed by : org.egothor.methodatlas.EvidencePackCommandTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.EvidencePackCommandTest]/[method:signingFailureLeavesAConsistentUnsignedPack(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

315

1.1
Location : buildSingle
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::buildSingle → KILLED

332

1.1
Location : buildHybrid
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : buildHybrid
Killed by : none
removed conditional - replaced equality check with false → TIMED_OUT

3.3
Location : buildHybrid
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

4.4
Location : buildHybrid
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
removed conditional - replaced equality check with true → KILLED

348

1.1
Location : buildHybrid
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::buildHybrid → KILLED

2.2
Location : lambda$buildHybrid$0
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:signsAndVerifiesWithHybridEd25519AndSphincsPlus()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::lambda$buildHybrid$0 → KILLED

365

1.1
Location : engineFactoryFor
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:verificationFailsWhenSignedEnvelopeIsTampered()]
Changed switch default to be first case → KILLED

2.2
Location : engineFactoryFor
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:verificationFailsWhenSignedEnvelopeIsTampered()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::engineFactoryFor → KILLED

382

1.1
Location : resolveAlias
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveAlias
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

383

1.1
Location : resolveAlias
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:derivesAlgorithmFromKeyringWhenUnspecified()]
replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::resolveAlias → KILLED

386

1.1
Location : resolveAlias
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : resolveAlias
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
removed conditional - replaced equality check with true → KILLED

389

1.1
Location : resolveAlias
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:usesFirstAliasWhenNoneRequested()]
replaced return value with "" for org/egothor/methodatlas/evidence/ZeroEchoSigner::resolveAlias → KILLED

403

1.1
Location : splitPair
Killed by : none
removed conditional - replaced comparison check with false → TIMED_OUT

2.2
Location : splitPair
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

3.3
Location : splitPair
Killed by : none
changed conditional boundary → TIMED_OUT

4.4
Location : splitPair
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:hybridSigningRejectsAliasWithoutSeparator()]
removed conditional - replaced comparison check with false → KILLED

5.5
Location : splitPair
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:hybridSigningRejectsAliasWithoutSeparator()]
removed conditional - replaced comparison check with true → KILLED

6.6
Location : splitPair
Killed by : none
Replaced integer subtraction with addition → TIMED_OUT

7.7
Location : splitPair
Killed by : none
removed conditional - replaced comparison check with true → TIMED_OUT

407

1.1
Location : splitPair
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
Replaced integer addition with subtraction → KILLED

408

1.1
Location : splitPair
Killed by : none
removed conditional - replaced equality check with false → TIMED_OUT

2.2
Location : splitPair
Killed by : none
removed conditional - replaced equality check with true → TIMED_OUT

3.3
Location : splitPair
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:hybridSigningRejectsAliasWithoutSeparator()]
removed conditional - replaced equality check with false → KILLED

4.4
Location : splitPair
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:hybridSigningRejectsAliasWithoutSeparator()]
removed conditional - replaced equality check with true → KILLED

411

1.1
Location : splitPair
Killed by : org.egothor.methodatlas.evidence.ZeroEchoSignerTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.evidence.ZeroEchoSignerTest]/[method:closeReleasesTheHybridSignatureContext()]
replaced return value with null for org/egothor/methodatlas/evidence/ZeroEchoSigner::splitPair → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1