9 Commits

Author SHA1 Message Date
adfa0b4b51 feat: add universal AsyncBus infrastructure
Introduce a generic asynchronous bus used for internal PKI workflows,
with resilient sweep support and symmetric primitive/wrapper type
compatibility for dispatch handling.

Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2026-01-02 22:28:53 +01:00
e01d95f48e chore: PMD 1.20.0 adaptation
Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2026-01-01 11:44:13 +01:00
969a846d95 fix: proxy object might be returned
Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2026-01-01 11:42:13 +01:00
d2ec77b8e3 feat: introduce SignatureWorkflow SPI and zeroecho-lib implementation
- add SignatureWorkflow SPI for asynchronous sign/verify operations
- define audit-friendly, exception-free failure model
- introduce stable OperationStatus, State and OperationResult semantics
- document trust boundaries, lifecycle, and audit constraints in SPI
  JavaDoc
- add ZeroEchoLibSignatureWorkflow backed by KeyringStore and ZeroEcho
  lib
- enforce opaque KeyRef handling and provider-local parsing
- add deterministic detail codes and UNKNOWN_OPERATION handling
- integrate workflow provider into ServiceLoader bootstrap
- align PkiBootstrap logging and defaults with crypto workflow SPI
- add comprehensive JUnit tests for validation and status semantics

Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2025-12-29 22:41:11 +01:00
0346c5b30f feat: refactor SPI/bootstrap to generic configurable providers
- Introduce a universal ConfigurableProvider/ProviderConfig abstraction
  for ServiceLoader-based components and align PKI bootstrapping
  utilities with it.
- Document deterministic provider selection, property-based
  configuration conventions, and security requirements (never log config
  values), including package-level documentation for spi, spi.store and
  spi.bootstrap.

fix: harden audit runtime, fix gzip scanning, add bounds and docs

- Fix FileAuditSink concatenated gzip scan by shielding underlying
  stream
- Use JUnit @TempDir for filesystem-backed tests
- Bound InMemoryAuditSink with deterministic ring buffer
- Add ServiceLoader smoke test and expand DefaultAuditService coverage
- Improve JavaDoc and logging across audit implementation

feat: add deterministic tests for PkiBootstrap with real SPI providers

- add JUnit 5 test suite for PkiBootstrap
- cover SPI selection for filesystem PkiStore and audit sinks
- use @TempDir for filesystem-backed providers
- register test ServiceLoader providers under src/test/resources
- ensure deterministic bootstrap behavior via system properties

Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2025-12-29 02:09:07 +01:00
cab1eeefe7 feat: add filesystem-based PkiStore reference implementation
Introduce a deterministic filesystem-backed PkiStore implementation
under zeroecho.pki.impl.fs.

Key characteristics:
- write-once semantics for immutable objects with explicit failure on
  overwrite
- history tracking for mutable records with full audit trail
- atomic writes using NIO (temp + move) with best-effort durability
- strict snapshot export supporting time-travel reconstruction
- configurable history retention (ON_WRITE policy)
- no secrets logged; JUL-only diagnostics for anomalies

Includes comprehensive JUnit 5 tests validating:
- write-once enforcement
- history creation and overwrite semantics
- strict snapshot export (failure and positive selection cases)
- deterministic on-disk layout and structure

This implementation is intentionally non-public and serves as a
reference and validation baseline for future persistence backends.

Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2025-12-28 01:15:46 +01:00
7673e7d82f feat: PKI module core design
Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2025-12-27 21:38:32 +01:00
276ac91eb4 chore: replace apache-cli deprecated methods
Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2025-12-27 16:55:28 +01:00
e82e0e57fb Merge hybrid cryptography support (KEX, derived keys, signatures)
This merge introduces the sdk.hybrid package with support for:
- hybrid key exchange (classic + post-quantum),
- hybrid-derived key injection for encryption and MAC builders,
- hybrid signature composition.

The implementation is additive at the SDK layer and does not modify
core cryptographic contracts.
2025-12-26 21:08:31 +01:00
224 changed files with 21312 additions and 126 deletions

View File

@@ -132,17 +132,17 @@ public final class CovertCommand {
* @throws ParseException if the arguments are invalid or incomplete * @throws ParseException if the arguments are invalid or incomplete
*/ */
public static int main(String[] args, Options options) throws ParseException { public static int main(String[] args, Options options) throws ParseException {
final Option EMBED_OPTION = Option.builder().longOpt("embed").desc("Embed a payload into a JPEG").build(); final Option EMBED_OPTION = Option.builder().longOpt("embed").desc("Embed a payload into a JPEG").get();
final Option EXTRACT_OPTION = Option.builder().longOpt("extract").desc("Extract a payload from a JPEG").build(); final Option EXTRACT_OPTION = Option.builder().longOpt("extract").desc("Extract a payload from a JPEG").get();
final Option JPEG_OPTION = Option.builder().longOpt("jpeg").hasArg().argName("input.jpg") final Option JPEG_OPTION = Option.builder().longOpt("jpeg").hasArg().argName("input.jpg")
.desc("Input JPEG file").required().build(); .desc("Input JPEG file").required().get();
final Option PAYLOAD_OPTION = Option.builder().longOpt("payload").hasArg().argName("payload.dat") final Option PAYLOAD_OPTION = Option.builder().longOpt("payload").hasArg().argName("payload.dat")
.desc("Binary payload file to embed").build(); .desc("Binary payload file to embed").get();
final Option OUTPUT_OPTION = Option.builder().longOpt("output").hasArg().argName("outputFile") final Option OUTPUT_OPTION = Option.builder().longOpt("output").hasArg().argName("outputFile")
.desc("Output JPEG or payload file").required().build(); .desc("Output JPEG or payload file").required().get();
final Option SLOTS_OPTION = Option.builder().longOpt("slots").hasArgs().valueSeparator(';') final Option SLOTS_OPTION = Option.builder().longOpt("slots").hasArgs().valueSeparator(';')
.argName("slot1;slot2;...") .argName("slot1;slot2;...")
.desc("Custom EXIF slots (e.g. Exif.UserComment:4096;Exif.Custom/tag=700,ascii,64,exif:1024)").build(); .desc("Custom EXIF slots (e.g. Exif.UserComment:4096;Exif.Custom/tag=700,ascii,64,exif:1024)").get();
OptionGroup modeGroup = new OptionGroup(); OptionGroup modeGroup = new OptionGroup();
modeGroup.addOption(EMBED_OPTION); modeGroup.addOption(EMBED_OPTION);

View File

@@ -128,71 +128,71 @@ public final class Guard {
// ---- operation selection // ---- operation selection
final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("in-file") final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("in-file")
.desc("Encrypt the given file").build(); .desc("Encrypt the given file").get();
final Option OPT_DECRYPT = Option.builder("d").longOpt("decrypt").hasArg().argName("in-file") final Option OPT_DECRYPT = Option.builder("d").longOpt("decrypt").hasArg().argName("in-file")
.desc("Decrypt the given file").build(); .desc("Decrypt the given file").get();
final OptionGroup OP = new OptionGroup().addOption(OPT_ENCRYPT).addOption(OPT_DECRYPT); final OptionGroup OP = new OptionGroup().addOption(OPT_ENCRYPT).addOption(OPT_DECRYPT);
OP.setRequired(true); OP.setRequired(true);
// ---- common I/O // ---- common I/O
final Option OPT_OUT = Option.builder("o").longOpt("output").hasArg().argName("out-file") final Option OPT_OUT = Option.builder("o").longOpt("output").hasArg().argName("out-file")
.desc("Output file (default: <in>.enc for encrypt, <in>.dec for decrypt)").build(); .desc("Output file (default: <in>.enc for encrypt, <in>.dec for decrypt)").get();
final Option OPT_KEYRING = Option.builder().longOpt("keyring").hasArg().argName("keyring.txt") final Option OPT_KEYRING = Option.builder().longOpt("keyring").hasArg().argName("keyring.txt")
.desc("KeyringStore file for aliases (required when aliases are used)").build(); .desc("KeyringStore file for aliases (required when aliases are used)").get();
// ---- payload selection and parameters // ---- payload selection and parameters
final Option OPT_ALG = Option.builder().longOpt("alg").hasArg().argName("name") final Option OPT_ALG = Option.builder().longOpt("alg").hasArg().argName("name")
.desc("Payload: aes-gcm | aes-ctr | aes-cbc-pkcs7 | aes-cbc-nopad | chacha-aead | chacha-stream " .desc("Payload: aes-gcm | aes-ctr | aes-cbc-pkcs7 | aes-cbc-nopad | chacha-aead | chacha-stream "
+ "(default: aes-gcm)") + "(default: aes-gcm)")
.build(); .get();
final Option OPT_AAD_HEX = Option.builder("a").longOpt("aad-hex").hasArg().argName("hex") final Option OPT_AAD_HEX = Option.builder("a").longOpt("aad-hex").hasArg().argName("hex")
.desc("Additional authenticated data as hex (AEAD modes)").build(); .desc("Additional authenticated data as hex (AEAD modes)").get();
final Option OPT_TAG_BITS = Option.builder().longOpt("tag-bits").hasArg().argName("96..128") final Option OPT_TAG_BITS = Option.builder().longOpt("tag-bits").hasArg().argName("96..128")
.desc("AES-GCM tag length in bits (default 128)").build(); .desc("AES-GCM tag length in bits (default 128)").get();
final Option OPT_NONCE_HEX = Option.builder().longOpt("nonce-hex").hasArg().argName("hex") final Option OPT_NONCE_HEX = Option.builder().longOpt("nonce-hex").hasArg().argName("hex")
.desc("ChaCha nonce (12-byte hex)").build(); .desc("ChaCha nonce (12-byte hex)").get();
final Option OPT_INIT_CTR = Option.builder().longOpt("init-ctr").hasArg().argName("int") final Option OPT_INIT_CTR = Option.builder().longOpt("init-ctr").hasArg().argName("int")
.desc("ChaCha stream initial counter (default 1)").build(); .desc("ChaCha stream initial counter (default 1)").get();
final Option OPT_CTR = Option.builder().longOpt("ctr").hasArg().argName("int") final Option OPT_CTR = Option.builder().longOpt("ctr").hasArg().argName("int")
.desc("ChaCha stream counter override (propagated via context)").build(); .desc("ChaCha stream counter override (propagated via context)").get();
final Option OPT_NO_HDR = Option.builder().longOpt("no-header") final Option OPT_NO_HDR = Option.builder().longOpt("no-header")
.desc("Do not write or expect a symmetric header").build(); .desc("Do not write or expect a symmetric header").get();
// ---- envelope parameters // ---- envelope parameters
final Option OPT_CEK_BYTES = Option.builder().longOpt("cek-bytes").hasArg().argName("len") final Option OPT_CEK_BYTES = Option.builder().longOpt("cek-bytes").hasArg().argName("len")
.desc("Payload key (CEK) length in bytes (default 32)").build(); .desc("Payload key (CEK) length in bytes (default 32)").get();
final Option OPT_MAX_RECIPS = Option.builder().longOpt("max-recipients").hasArg().argName("n") final Option OPT_MAX_RECIPS = Option.builder().longOpt("max-recipients").hasArg().argName("n")
.desc("Max recipients in the envelope header (default 64)").build(); .desc("Max recipients in the envelope header (default 64)").get();
final Option OPT_MAX_ENTRY = Option.builder().longOpt("max-entry-len").hasArg().argName("bytes") final Option OPT_MAX_ENTRY = Option.builder().longOpt("max-entry-len").hasArg().argName("bytes")
.desc("Max single recipient-entry length (default 1048576)").build(); .desc("Max single recipient-entry length (default 1048576)").get();
final Option OPT_NO_SHUFFLE = Option.builder().longOpt("no-shuffle") final Option OPT_NO_SHUFFLE = Option.builder().longOpt("no-shuffle")
.desc("Disable shuffling of recipients (enabled by default)").build(); .desc("Disable shuffling of recipients (enabled by default)").get();
// ---- recipients (real) // ---- recipients (real)
final Option OPT_TO_ALIAS = Option.builder().longOpt("to-alias").hasArg().argName("alias") final Option OPT_TO_ALIAS = Option.builder().longOpt("to-alias").hasArg().argName("alias")
.desc("Add recipient by alias from keyring (repeatable)").build(); .desc("Add recipient by alias from keyring (repeatable)").get();
final Option OPT_TO_PSW = Option.builder().longOpt("to-psw").hasArg().argName("password") final Option OPT_TO_PSW = Option.builder().longOpt("to-psw").hasArg().argName("password")
.desc("Add password recipient (repeatable)").build(); .desc("Add password recipient (repeatable)").get();
final Option OPT_PSW_ITER = Option.builder().longOpt("to-iter").hasArg().argName("n") final Option OPT_PSW_ITER = Option.builder().longOpt("to-iter").hasArg().argName("n")
.desc("PBKDF2 iterations for password recipients (default 200000)").build(); .desc("PBKDF2 iterations for password recipients (default 200000)").get();
final Option OPT_PSW_SALT = Option.builder().longOpt("to-salt-len").hasArg().argName("bytes") final Option OPT_PSW_SALT = Option.builder().longOpt("to-salt-len").hasArg().argName("bytes")
.desc("PBKDF2 salt length for password recipients (default 16)").build(); .desc("PBKDF2 salt length for password recipients (default 16)").get();
final Option OPT_PSW_KEK = Option.builder().longOpt("to-kek-bytes").hasArg().argName("bytes") final Option OPT_PSW_KEK = Option.builder().longOpt("to-kek-bytes").hasArg().argName("bytes")
.desc("Derived KEK length for password recipients (default 32)").build(); .desc("Derived KEK length for password recipients (default 32)").get();
// ---- decoys (all types) // ---- decoys (all types)
final Option OPT_DECOY_ALIAS = Option.builder().longOpt("decoy-alias").hasArg().argName("alias") final Option OPT_DECOY_ALIAS = Option.builder().longOpt("decoy-alias").hasArg().argName("alias")
.desc("Add a decoy recipient from keyring (repeatable)").build(); .desc("Add a decoy recipient from keyring (repeatable)").get();
final Option OPT_DECOY_PSW = Option.builder().longOpt("decoy-psw").hasArg().argName("password") final Option OPT_DECOY_PSW = Option.builder().longOpt("decoy-psw").hasArg().argName("password")
.desc("Add a decoy password recipient (repeatable)").build(); .desc("Add a decoy password recipient (repeatable)").get();
final Option OPT_DECOY_PSW_RAND = Option.builder().longOpt("decoy-psw-rand").hasArg().argName("n") final Option OPT_DECOY_PSW_RAND = Option.builder().longOpt("decoy-psw-rand").hasArg().argName("n")
.desc("Add N random decoy password recipients").build(); .desc("Add N random decoy password recipients").get();
// ---- unlock (decrypt) // ---- unlock (decrypt)
final Option OPT_PRIV_ALIAS = Option.builder().longOpt("priv-alias").hasArg().argName("alias") final Option OPT_PRIV_ALIAS = Option.builder().longOpt("priv-alias").hasArg().argName("alias")
.desc("Unlock with private key from keyring").build(); .desc("Unlock with private key from keyring").get();
final Option OPT_PASSWORD = Option.builder("p").longOpt("password").hasArg().argName("password") final Option OPT_PASSWORD = Option.builder("p").longOpt("password").hasArg().argName("password")
.desc("Unlock with password").build(); .desc("Unlock with password").get();
options.addOptionGroup(OP); options.addOptionGroup(OP);
options.addOption(OPT_OUT); options.addOption(OPT_OUT);

View File

@@ -113,91 +113,91 @@ public final class Kem { // NOPMD
/** Encrypt mode: -e|--encrypt &lt;input&gt; */ /** Encrypt mode: -e|--encrypt &lt;input&gt; */
public static final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("input") public static final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("input")
.desc("Encrypt the input file").build(); .desc("Encrypt the input file").get();
/** Decrypt mode: -d|--decrypt &lt;input&gt; */ /** Decrypt mode: -d|--decrypt &lt;input&gt; */
public static final Option OPT_DECRYPT = Option.builder("d").longOpt("decrypt").hasArg().argName("input") public static final Option OPT_DECRYPT = Option.builder("d").longOpt("decrypt").hasArg().argName("input")
.desc("Decrypt the input file").build(); .desc("Decrypt the input file").get();
/** Output path: -o|--output &lt;file&gt; */ /** Output path: -o|--output &lt;file&gt; */
public static final Option OPT_OUTPUT = Option.builder("o").longOpt("output").hasArg().argName("file") public static final Option OPT_OUTPUT = Option.builder("o").longOpt("output").hasArg().argName("file")
.desc("Output file path (default: &lt;input&gt;.enc for encrypt, &lt;input&gt;.dec for decrypt)").build(); .desc("Output file path (default: &lt;input&gt;.enc for encrypt, &lt;input&gt;.dec for decrypt)").get();
/** Keyring path: -K|--keyring &lt;file&gt; */ /** Keyring path: -K|--keyring &lt;file&gt; */
public static final Option OPT_KEYRING = Option.builder("K").longOpt("keyring").hasArg().argName("file") public static final Option OPT_KEYRING = Option.builder("K").longOpt("keyring").hasArg().argName("file")
.desc("Path to KeyringStore file").build(); .desc("Path to KeyringStore file").get();
/** Recipient public alias (encrypt): --pub &lt;alias&gt; */ /** Recipient public alias (encrypt): --pub &lt;alias&gt; */
public static final Option OPT_PUB = Option.builder().longOpt("pub").hasArg().argName("alias") public static final Option OPT_PUB = Option.builder().longOpt("pub").hasArg().argName("alias")
.desc("Recipient public key alias (encryption)").build(); .desc("Recipient public key alias (encryption)").get();
/** Recipient private alias (decrypt): --priv &lt;alias&gt; */ /** Recipient private alias (decrypt): --priv &lt;alias&gt; */
public static final Option OPT_PRIV = Option.builder().longOpt("priv").hasArg().argName("alias") public static final Option OPT_PRIV = Option.builder().longOpt("priv").hasArg().argName("alias")
.desc("Recipient private key alias (decryption)").build(); .desc("Recipient private key alias (decryption)").get();
/** KEM id: --kem &lt;id&gt; */ /** KEM id: --kem &lt;id&gt; */
public static final Option OPT_KEM = Option.builder().longOpt("kem").hasArg().argName("id") public static final Option OPT_KEM = Option.builder().longOpt("kem").hasArg().argName("id")
.desc("KEM algorithm id (see --list-kems)").build(); .desc("KEM algorithm id (see --list-kems)").get();
/** Discovery: --list-kems */ /** Discovery: --list-kems */
public static final Option OPT_LIST_KEMS = Option.builder().longOpt("list-kems") public static final Option OPT_LIST_KEMS = Option.builder().longOpt("list-kems")
.desc("List KEM algorithms that support ENCAPSULATE and DECAPSULATE and exit").build(); .desc("List KEM algorithms that support ENCAPSULATE and DECAPSULATE and exit").get();
/** Payload switch: --aes */ /** Payload switch: --aes */
public static final Option OPT_AES = Option.builder().longOpt("aes") public static final Option OPT_AES = Option.builder().longOpt("aes")
.desc("Use AES payload (select mode via --aes-cipher)").build(); .desc("Use AES payload (select mode via --aes-cipher)").get();
/** Payload switch: --chacha */ /** Payload switch: --chacha */
public static final Option OPT_CHACHA = Option.builder().longOpt("chacha") public static final Option OPT_CHACHA = Option.builder().longOpt("chacha")
.desc("Use ChaCha payload (AEAD if --aad is provided, otherwise stream)").build(); .desc("Use ChaCha payload (AEAD if --aad is provided, otherwise stream)").get();
/** AAD (hex): --aad &lt;hex&gt; */ /** AAD (hex): --aad &lt;hex&gt; */
public static final Option OPT_AAD = Option.builder().longOpt("aad").hasArg().argName("hex") public static final Option OPT_AAD = Option.builder().longOpt("aad").hasArg().argName("hex")
.desc("Additional Authenticated Data (hex)").build(); .desc("Additional Authenticated Data (hex)").get();
/** Header toggle: --header */ /** Header toggle: --header */
public static final Option OPT_HEADER = Option.builder().longOpt("header") public static final Option OPT_HEADER = Option.builder().longOpt("header")
.desc("Write/read a compact symmetric header (IV/AAD/params) when supported").build(); .desc("Write/read a compact symmetric header (IV/AAD/params) when supported").get();
/** HKDF: --hkdf [infoHex] */ /** HKDF: --hkdf [infoHex] */
public static final Option OPT_HKDF = Option.builder().longOpt("hkdf").optionalArg(true).hasArg().argName("infoHex") public static final Option OPT_HKDF = Option.builder().longOpt("hkdf").optionalArg(true).hasArg().argName("infoHex")
.desc("Use HKDF-SHA256 for KEM secret; optional info as hex (default internal info)").build(); .desc("Use HKDF-SHA256 for KEM secret; optional info as hex (default internal info)").get();
/** Direct secret: --direct */ /** Direct secret: --direct */
public static final Option OPT_DIRECT = Option.builder().longOpt("direct") public static final Option OPT_DIRECT = Option.builder().longOpt("direct")
.desc("Use the raw KEM shared secret directly (disable HKDF)").build(); .desc("Use the raw KEM shared secret directly (disable HKDF)").get();
/** Derived key bytes: --key-bytes &lt;int&gt; */ /** Derived key bytes: --key-bytes &lt;int&gt; */
public static final Option OPT_KEY_BYTES = Option.builder().longOpt("key-bytes").hasArg().argName("int") public static final Option OPT_KEY_BYTES = Option.builder().longOpt("key-bytes").hasArg().argName("int")
.type(Number.class).desc("Derived symmetric key length in bytes (default 32)").build(); .type(Number.class).desc("Derived symmetric key length in bytes (default 32)").get();
/** Max KEM ciphertext len: --max-kem-ct &lt;int&gt; */ /** Max KEM ciphertext len: --max-kem-ct &lt;int&gt; */
public static final Option OPT_MAX_KEM_CT = Option.builder().longOpt("max-kem-ct").hasArg().argName("int") public static final Option OPT_MAX_KEM_CT = Option.builder().longOpt("max-kem-ct").hasArg().argName("int")
.type(Number.class).desc("Maximum accepted KEM ciphertext length in bytes (default 65536)").build(); .type(Number.class).desc("Maximum accepted KEM ciphertext length in bytes (default 65536)").get();
/** AES mode: --aes-cipher gcm|ctr|cbc */ /** AES mode: --aes-cipher gcm|ctr|cbc */
public static final Option OPT_AES_CIPHER = Option.builder().longOpt("aes-cipher").hasArg().argName("gcm|ctr|cbc") public static final Option OPT_AES_CIPHER = Option.builder().longOpt("aes-cipher").hasArg().argName("gcm|ctr|cbc")
.desc("AES cipher variant for payload (default gcm)").build(); .desc("AES cipher variant for payload (default gcm)").get();
/** AES IV: --aes-iv &lt;hex&gt; */ /** AES IV: --aes-iv &lt;hex&gt; */
public static final Option OPT_AES_IV = Option.builder().longOpt("aes-iv").hasArg().argName("hex") public static final Option OPT_AES_IV = Option.builder().longOpt("aes-iv").hasArg().argName("hex")
.desc("AES IV/nonce (hex)").build(); .desc("AES IV/nonce (hex)").get();
/** AES tag bits: --aes-tag-bits &lt;int&gt; */ /** AES tag bits: --aes-tag-bits &lt;int&gt; */
public static final Option OPT_AES_TAG_BITS = Option.builder().longOpt("aes-tag-bits").hasArg().argName("int") public static final Option OPT_AES_TAG_BITS = Option.builder().longOpt("aes-tag-bits").hasArg().argName("int")
.type(Number.class).desc("AES-GCM authentication tag length in bits (default 128)").build(); .type(Number.class).desc("AES-GCM authentication tag length in bits (default 128)").get();
/** ChaCha nonce: --chacha-nonce &lt;hex&gt; */ /** ChaCha nonce: --chacha-nonce &lt;hex&gt; */
public static final Option OPT_CHACHA_NONCE = Option.builder().longOpt("chacha-nonce").hasArg().argName("hex") public static final Option OPT_CHACHA_NONCE = Option.builder().longOpt("chacha-nonce").hasArg().argName("hex")
.desc("ChaCha nonce (hex, usually 12 bytes)").build(); .desc("ChaCha nonce (hex, usually 12 bytes)").get();
/** ChaCha counter value: --chacha-counter &lt;int&gt; */ /** ChaCha counter value: --chacha-counter &lt;int&gt; */
public static final Option OPT_CHACHA_COUNTER = Option.builder().longOpt("chacha-counter").hasArg().argName("int") public static final Option OPT_CHACHA_COUNTER = Option.builder().longOpt("chacha-counter").hasArg().argName("int")
.type(Number.class).desc("ChaCha counter value for stream mode (integer)").build(); .type(Number.class).desc("ChaCha counter value for stream mode (integer)").get();
/** ChaCha initial counter: --chacha-initial &lt;int&gt; */ /** ChaCha initial counter: --chacha-initial &lt;int&gt; */
public static final Option OPT_CHACHA_INITIAL = Option.builder().longOpt("chacha-initial").hasArg().argName("int") public static final Option OPT_CHACHA_INITIAL = Option.builder().longOpt("chacha-initial").hasArg().argName("int")
.type(Number.class).desc("ChaCha initial counter (integer)").build(); .type(Number.class).desc("ChaCha initial counter (integer)").get();
private Kem() { private Kem() {
// no instances // no instances

View File

@@ -167,49 +167,49 @@ public final class KeyStoreManagement { // NOPMD
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
private static final Option KEYSTORE_OPTION = Option.builder("k").longOpt("keystore").hasArg().argName("file") private static final Option KEYSTORE_OPTION = Option.builder("k").longOpt("keystore").hasArg().argName("file")
.desc("Path to keyring store").build(); .desc("Path to keyring store").get();
private static final Option LIST_ALGORITHMS_OPTION = Option.builder().longOpt("list-algorithms") private static final Option LIST_ALGORITHMS_OPTION = Option.builder().longOpt("list-algorithms")
.desc("List catalog algorithms with symmetric/asymmetric support").build(); .desc("List catalog algorithms with symmetric/asymmetric support").get();
private static final Option LIST_ALIASES_OPTION = Option.builder().longOpt("list-aliases") private static final Option LIST_ALIASES_OPTION = Option.builder().longOpt("list-aliases")
.desc("List aliases present in the keyring").build(); .desc("List aliases present in the keyring").get();
private static final Option GENERATE_OPTION = Option.builder().longOpt("generate") private static final Option GENERATE_OPTION = Option.builder().longOpt("generate")
.desc("Generate a keypair or a secret").build(); .desc("Generate a keypair or a secret").get();
private static final Option ALG_OPTION = Option.builder().longOpt("alg").hasArg().argName("id") private static final Option ALG_OPTION = Option.builder().longOpt("alg").hasArg().argName("id")
.desc("Algorithm id (e.g., RSA, Ed25519, AES, Frodo)").build(); .desc("Algorithm id (e.g., RSA, Ed25519, AES, Frodo)").get();
private static final Option ALIAS_OPTION = Option.builder().longOpt("alias").hasArg().argName("name") private static final Option ALIAS_OPTION = Option.builder().longOpt("alias").hasArg().argName("name")
.desc("Alias base; for asymmetric, two entries will be written").build(); .desc("Alias base; for asymmetric, two entries will be written").get();
private static final Option KIND_OPTION = Option.builder().longOpt("kind").hasArg().argName("sym|asym") private static final Option KIND_OPTION = Option.builder().longOpt("kind").hasArg().argName("sym|asym")
.desc("Force symmetric or asymmetric when algorithm supports both").build(); .desc("Force symmetric or asymmetric when algorithm supports both").get();
private static final Option PUB_SUFFIX_OPTION = Option.builder().longOpt("pub-suffix").hasArg().argName("sfx") private static final Option PUB_SUFFIX_OPTION = Option.builder().longOpt("pub-suffix").hasArg().argName("sfx")
.desc("Suffix for public alias (default .pub)").build(); .desc("Suffix for public alias (default .pub)").get();
private static final Option PRV_SUFFIX_OPTION = Option.builder().longOpt("prv-suffix").hasArg().argName("sfx") private static final Option PRV_SUFFIX_OPTION = Option.builder().longOpt("prv-suffix").hasArg().argName("sfx")
.desc("Suffix for private alias (default .prv)").build(); .desc("Suffix for private alias (default .prv)").get();
private static final Option OVERWRITE_OPTION = Option.builder().longOpt("overwrite") private static final Option OVERWRITE_OPTION = Option.builder().longOpt("overwrite")
.desc("Overwrite existing aliases on conflict").build(); .desc("Overwrite existing aliases on conflict").get();
private static final Option EXPORT_OPTION = Option.builder().longOpt("export") private static final Option EXPORT_OPTION = Option.builder().longOpt("export")
.desc("Export selected aliases as a versioned text snippet").build(); .desc("Export selected aliases as a versioned text snippet").get();
private static final Option IMPORT_OPTION = Option.builder().longOpt("import") private static final Option IMPORT_OPTION = Option.builder().longOpt("import")
.desc("Import a versioned text snippet into the keyring").build(); .desc("Import a versioned text snippet into the keyring").get();
private static final Option ALIASES_OPTION = Option.builder().longOpt("aliases").hasArg().argName("a,b,c") private static final Option ALIASES_OPTION = Option.builder().longOpt("aliases").hasArg().argName("a,b,c")
.desc("Comma-separated aliases to export; empty means all").build(); .desc("Comma-separated aliases to export; empty means all").get();
private static final Option OUTFILE_OPTION = Option.builder().longOpt("out").hasArg().argName("file|-") private static final Option OUTFILE_OPTION = Option.builder().longOpt("out").hasArg().argName("file|-")
.desc("Output file for export (default '-' for stdout)").build(); .desc("Output file for export (default '-' for stdout)").get();
private static final Option INFILE_OPTION = Option.builder().longOpt("in").hasArg().argName("file|-") private static final Option INFILE_OPTION = Option.builder().longOpt("in").hasArg().argName("file|-")
.desc("Input file for import (default '-' for stdin)").build(); .desc("Input file for import (default '-' for stdin)").get();
/** Prevents instantiation. */ /** Prevents instantiation. */
private KeyStoreManagement() { private KeyStoreManagement() {

View File

@@ -48,10 +48,10 @@ import java.util.Objects;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.help.HelpFormatter;
import zeroecho.core.alg.digest.DigestSpec; import zeroecho.core.alg.digest.DigestSpec;
import zeroecho.core.err.VerificationException; import zeroecho.core.err.VerificationException;
@@ -103,28 +103,28 @@ public final class Tag { // NOPMD
// ---- All options as constants // ---- All options as constants
private static final Option TYPE_OPT = Option.builder().longOpt("type").hasArg().argName("signature|digest") private static final Option TYPE_OPT = Option.builder().longOpt("type").hasArg().argName("signature|digest")
.desc("tag primitive type").build(); .desc("tag primitive type").get();
private static final Option MODE_OPT = Option.builder().longOpt("mode").hasArg().argName("produce|verify") private static final Option MODE_OPT = Option.builder().longOpt("mode").hasArg().argName("produce|verify")
.desc("operation mode").build(); .desc("operation mode").get();
private static final Option ALG_OPT = Option.builder().longOpt("alg").hasArg().argName("id") private static final Option ALG_OPT = Option.builder().longOpt("alg").hasArg().argName("id")
.desc("algorithm id (signature: Ed25519/Ed448/ECDSA/RSA; digest: SHA-256/.../SHAKE256:N)").build(); .desc("algorithm id (signature: Ed25519/Ed448/ECDSA/RSA; digest: SHA-256/.../SHAKE256:N)").get();
private static final Option KS_OPT = Option.builder().longOpt("ks").hasArg().argName("file") private static final Option KS_OPT = Option.builder().longOpt("ks").hasArg().argName("file")
.desc("keyring file (required for signature)").build(); .desc("keyring file (required for signature)").get();
private static final Option PRIV_OPT = Option.builder().longOpt("priv").hasArg().argName("alias") private static final Option PRIV_OPT = Option.builder().longOpt("priv").hasArg().argName("alias")
.desc("private key alias (signature + produce)").build(); .desc("private key alias (signature + produce)").get();
private static final Option PUB_OPT = Option.builder().longOpt("pub").hasArg().argName("alias") private static final Option PUB_OPT = Option.builder().longOpt("pub").hasArg().argName("alias")
.desc("public key alias (signature + verify)").build(); .desc("public key alias (signature + verify)").get();
private static final Option IN_OPT = Option.builder().longOpt("in").hasArg().argName("file|-") private static final Option IN_OPT = Option.builder().longOpt("in").hasArg().argName("file|-")
.desc("input file or - for STDIN").build(); .desc("input file or - for STDIN").get();
private static final Option OUT_OPT = Option.builder().longOpt("out").hasArg().argName("file|-") private static final Option OUT_OPT = Option.builder().longOpt("out").hasArg().argName("file|-")
.desc("output file or - for STDOUT").build(); .desc("output file or - for STDOUT").get();
// ---- Allowed values and defaults (no enums) // ---- Allowed values and defaults (no enums)
private static final String TYPE_SIGNATURE = "signature"; private static final String TYPE_SIGNATURE = "signature";
@@ -172,7 +172,7 @@ public final class Tag { // NOPMD
if (!has(cli, TYPE_OPT) || !has(cli, MODE_OPT) || !has(cli, ALG_OPT) || !has(cli, IN_OPT) if (!has(cli, TYPE_OPT) || !has(cli, MODE_OPT) || !has(cli, ALG_OPT) || !has(cli, IN_OPT)
|| !has(cli, OUT_OPT)) { || !has(cli, OUT_OPT)) {
new HelpFormatter().printHelp("zeroecho -T [options]", opts); HelpFormatter.builder().get().printHelp("zeroecho -T [options]", "", opts, "", false);
return 2; return 2;
} }

View File

@@ -41,12 +41,12 @@ import java.util.logging.Logger;
import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.MissingOptionException; import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.help.HelpFormatter;
import zeroecho.sdk.util.BouncyCastleActivator; import zeroecho.sdk.util.BouncyCastleActivator;
@@ -98,8 +98,9 @@ public final class ZeroEcho {
* prints the help message. * prints the help message.
* *
* @param args command-line arguments passed to the program * @param args command-line arguments passed to the program
* @throws IOException If the output could not be written
*/ */
public static void main(final String[] args) { public static void main(final String[] args) throws IOException {
final int errorCode = mainProcess(args); final int errorCode = mainProcess(args);
if (errorCode == 0) { if (errorCode == 0) {
@@ -116,15 +117,16 @@ public final class ZeroEcho {
* *
* @param args command-line arguments passed to the program * @param args command-line arguments passed to the program
* @return error-code * @return error-code
* @throws IOException If the output could not be written
*/ */
public static int mainProcess(final String... args) { public static int mainProcess(final String... args) throws IOException {
final Option KEM_OPTION = Option.builder("E").longOpt("kem").desc("KEM encryption/decryption").build(); final Option KEM_OPTION = Option.builder("E").longOpt("kem").desc("KEM encryption/decryption").get();
final Option GUARD_OPTION = Option.builder("G").longOpt("guard") final Option GUARD_OPTION = Option.builder("G").longOpt("guard")
.desc("multi-recipient encryption/decryption (keys+passwords), AES/ChaCha").build(); .desc("multi-recipient encryption/decryption (keys+passwords), AES/ChaCha").get();
final Option KEYSTORE_OPTION = Option.builder("K").longOpt("ksm").desc("key store management").build(); final Option KEYSTORE_OPTION = Option.builder("K").longOpt("ksm").desc("key store management").get();
final Option COVERT_OPTION = Option.builder("C").longOpt("covert").desc("covert channel processing").build(); final Option COVERT_OPTION = Option.builder("C").longOpt("covert").desc("covert channel processing").get();
final Option TAG_OPTION = Option.builder("T").longOpt("tag") final Option TAG_OPTION = Option.builder("T").longOpt("tag")
.desc("tag subcommand (signature/digest; produce/verify)").build(); .desc("tag subcommand (signature/digest; produce/verify)").get();
final OptionGroup OPERATION_GROUP = new OptionGroup(); final OptionGroup OPERATION_GROUP = new OptionGroup();
OPERATION_GROUP.addOption(GUARD_OPTION); OPERATION_GROUP.addOption(GUARD_OPTION);
@@ -186,12 +188,12 @@ public final class ZeroEcho {
* @param options The {@link Options} instance defining the available * @param options The {@link Options} instance defining the available
* command-line options. * command-line options.
* @return always {@code 1} * @return always {@code 1}
* @throws IOException If the output could not be written
*/ */
private static int help(final Options options) { private static int help(final Options options) throws IOException {
// automatically generate the help statement // automatically generate the help statement
final HelpFormatter formatter = new HelpFormatter(); final HelpFormatter formatter = HelpFormatter.builder().get();
formatter.setWidth(80); formatter.printHelp(ZeroEcho.class.getName(), "", options, "", false);
formatter.printHelp(ZeroEcho.class.getName(), options);
return 1; return 1;
} }

View File

@@ -36,12 +36,14 @@ package zeroecho;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
class ZeroEchoTest { class ZeroEchoTest {
@Test @Test
void testAsymetricOptionWithoutParamsReturnsOne() { void testAsymetricOptionWithoutParamsReturnsOne() throws IOException {
System.out.println("testAsymetricOptionWithoutParamsReturnsOne"); System.out.println("testAsymetricOptionWithoutParamsReturnsOne");
int result = ZeroEcho.mainProcess(new String[] { "-A" }); int result = ZeroEcho.mainProcess(new String[] { "-A" });
assertEquals(1, result, "Asymetric option without parameters should return 1 (error/help)"); assertEquals(1, result, "Asymetric option without parameters should return 1 (error/help)");
@@ -49,7 +51,7 @@ class ZeroEchoTest {
} }
@Test @Test
void testAesPswOptionWithoutParamsReturnsOne() { void testAesPswOptionWithoutParamsReturnsOne() throws IOException {
System.out.println("testAesPswOptionWithoutParamsReturnsOne"); System.out.println("testAesPswOptionWithoutParamsReturnsOne");
int result = ZeroEcho.mainProcess(new String[] { "-P" }); int result = ZeroEcho.mainProcess(new String[] { "-P" });
assertEquals(1, result, "AES-PSW option without parameters should return 1 (error/help)"); assertEquals(1, result, "AES-PSW option without parameters should return 1 (error/help)");
@@ -57,7 +59,7 @@ class ZeroEchoTest {
} }
@Test @Test
void testNoOptionReturnsOne() { void testNoOptionReturnsOne() throws IOException {
System.out.println("testNoOptionReturnsOne"); System.out.println("testNoOptionReturnsOne");
int result = ZeroEcho.mainProcess(new String[] {}); int result = ZeroEcho.mainProcess(new String[] {});
assertEquals(1, result, "No options should return 1 (error/help)"); assertEquals(1, result, "No options should return 1 (error/help)");
@@ -65,7 +67,7 @@ class ZeroEchoTest {
} }
@Test @Test
void testInvalidOptionReturnsOne() { void testInvalidOptionReturnsOne() throws IOException {
System.out.println("testInvalidOptionReturnsOne"); System.out.println("testInvalidOptionReturnsOne");
int result = ZeroEcho.mainProcess(new String[] { "-X" }); int result = ZeroEcho.mainProcess(new String[] { "-X" });
assertEquals(1, result, "Invalid option should return 1 (error/help)"); assertEquals(1, result, "Invalid option should return 1 (error/help)");

View File

@@ -45,7 +45,7 @@ dependencies {
pmd { pmd {
consoleOutput = true consoleOutput = true
toolVersion = '7.19.0' toolVersion = '7.20.0'
sourceSets = [sourceSets.main] sourceSets = [sourceSets.main]
ruleSetFiles = files(rootProject.file(".ruleset")) ruleSetFiles = files(rootProject.file(".ruleset"))
} }

View File

@@ -48,10 +48,9 @@ import zeroecho.core.spi.AsymmetricKeyBuilder;
* *
* <p> * <p>
* This builder maps {@link SlhDsaKeyGenSpec} to the appropriate * This builder maps {@link SlhDsaKeyGenSpec} to the appropriate
* {@code org.bouncycastle.jcajce.spec.SLHDSAParameterSpec} constant. * {@code org.bouncycastle.jcajce.spec.SLHDSAParameterSpec} constant. Reflection
* :contentReference[oaicite:3]{index=3} Reflection is used to avoid a hard * is used to avoid a hard dependency on any particular set of parameter
* dependency on any particular set of parameter constants across provider * constants across provider versions.
* versions.
* </p> * </p>
* *
* @since 1.0 * @since 1.0
@@ -103,7 +102,6 @@ public final class SlhDsaKeyGenBuilder implements AsymmetricKeyBuilder<SlhDsaKey
// Optional pre-hash suffix used by BC: // Optional pre-hash suffix used by BC:
// _with_sha256/_with_sha512/_with_shake128/_with_shake256 // _with_sha256/_with_sha512/_with_shake128/_with_shake256
// :contentReference[oaicite:4]{index=4}
String suffix = ""; String suffix = "";
if (spec.preHash() != SlhDsaKeyGenSpec.PreHash.NONE) { if (spec.preHash() != SlhDsaKeyGenSpec.PreHash.NONE) {
suffix = "_with_" + spec.preHash().name().toLowerCase(Locale.ROOT); suffix = "_with_" + spec.preHash().name().toLowerCase(Locale.ROOT);

View File

@@ -106,7 +106,6 @@ public final class SlhDsaKeyGenSpec implements AlgorithmKeySpec {
* <p> * <p>
* Bouncy Castle exposes "with hash" variants as distinct parameter specs, for * Bouncy Castle exposes "with hash" variants as distinct parameter specs, for
* example {@code slh_dsa_sha2_128s_with_sha256}. * example {@code slh_dsa_sha2_128s_with_sha256}.
* :contentReference[oaicite:1]{index=1}
* </p> * </p>
* *
* <p> * <p>
@@ -183,7 +182,6 @@ public final class SlhDsaKeyGenSpec implements AlgorithmKeySpec {
* <p> * <p>
* This bypasses automatic mapping. The name must match a static field in * This bypasses automatic mapping. The name must match a static field in
* {@code org.bouncycastle.jcajce.spec.SLHDSAParameterSpec}. * {@code org.bouncycastle.jcajce.spec.SLHDSAParameterSpec}.
* :contentReference[oaicite:2]{index=2}
* </p> * </p>
* *
* @param name field name in {@code SLHDSAParameterSpec} * @param name field name in {@code SLHDSAParameterSpec}

View File

@@ -103,10 +103,10 @@ public class ByteVerificationStrategy extends VerificationBiPredicate<byte[]> {
if (LOG.isLoggable(Level.FINE)) { if (LOG.isLoggable(Level.FINE)) {
if (r == 0) { if (r == 0) {
LOG.log(Level.FINE, "PASS {0} == {1}", // NOPMD LOG.log(Level.FINE, "PASS {0} == {1}",
new Object[] { Strings.toShortString(a), Strings.toShortString(b) }); new Object[] { Strings.toShortString(a), Strings.toShortString(b) });
} else { } else {
LOG.log(Level.FINE, "FAIL {0} != {1}", // NOPMD LOG.log(Level.FINE, "FAIL {0} != {1}",
new Object[] { Strings.toShortString(a), Strings.toShortString(b) }); new Object[] { Strings.toShortString(a), Strings.toShortString(b) });
} }
} }

View File

@@ -103,9 +103,9 @@ public class SignatureVerificationStrategy extends VerificationBiPredicate<Signa
if (LOG.isLoggable(Level.FINE)) { if (LOG.isLoggable(Level.FINE)) {
if (result) { if (result) {
LOG.log(Level.FINE, "PASS {0}", Strings.toShortString(b)); // NOPMD LOG.log(Level.FINE, "PASS {0}", Strings.toShortString(b));
} else { } else {
LOG.log(Level.FINE, "FAIL {0}", Strings.toShortString(b)); // NOPMD LOG.log(Level.FINE, "FAIL {0}", Strings.toShortString(b));
} }
} }

View File

@@ -120,16 +120,7 @@ public final class MldsaLargeDataTest {
KeyPair kp = CryptoAlgorithms.keyPair("ML-DSA", spec); KeyPair kp = CryptoAlgorithms.keyPair("ML-DSA", spec);
SignatureContext verifierCtx = CryptoAlgorithms.create("ML-DSA", KeyUsage.VERIFY, kp.getPublic()); SignatureContext mldsaVerifier = CryptoAlgorithms.create("ML-DSA", KeyUsage.VERIFY, kp.getPublic());
if (!(verifierCtx instanceof MldsaSignatureContext mldsaVerifier)) {
try {
verifierCtx.close();
} catch (Exception ignore) {
}
throw new AssertionError(
"VERIFY context must be MldsaSignatureContext, got: " + verifierCtx.getClass().getName());
}
int expectedSigLen = mldsaVerifier.tagLength(); int expectedSigLen = mldsaVerifier.tagLength();
System.out.println(INDENT + " expectedSigLen=" + expectedSigLen); System.out.println(INDENT + " expectedSigLen=" + expectedSigLen);
@@ -188,15 +179,7 @@ public final class MldsaLargeDataTest {
byte[] badSig = Arrays.copyOf(signature, signature.length); byte[] badSig = Arrays.copyOf(signature, signature.length);
badSig[0] = (byte) (badSig[0] ^ 0x01); badSig[0] = (byte) (badSig[0] ^ 0x01);
SignatureContext badVerifierCtx = CryptoAlgorithms.create("ML-DSA", KeyUsage.VERIFY, kp.getPublic()); SignatureContext badVerifier = CryptoAlgorithms.create("ML-DSA", KeyUsage.VERIFY, kp.getPublic());
if (!(badVerifierCtx instanceof MldsaSignatureContext badVerifier)) {
try {
badVerifierCtx.close();
} catch (Exception ignore) {
}
throw new AssertionError("VERIFY context must be MldsaSignatureContext (negative), got: "
+ badVerifierCtx.getClass().getName());
}
try { try {
badVerifier.setExpectedTag(badSig); badVerifier.setExpectedTag(badSig);

32
pki/.classpath Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="src/main/java">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/main" path="src/main/resources">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/resources">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

23
pki/.project Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>pki</name>
<comment>Project pki created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

31
pki/LICENSE Normal file
View File

@@ -0,0 +1,31 @@
Copyright (C) 2025, Leo Galambos
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
display the following acknowledgement:
This product includes software developed by the Egothor project.
4. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

57
pki/build.gradle Normal file
View File

@@ -0,0 +1,57 @@
plugins {
id 'buildlogic.java-application-conventions'
id 'com.palantir.git-version'
}
group 'org.egothor'
dependencies {
implementation 'org.apache.commons:commons-text'
implementation 'commons-cli:commons-cli'
implementation project(':lib')
}
application {
// Define the main class for the application.
mainClass = 'zeroecho.pki.PkiApplication'
}
jar {
manifest {
attributes(
'Main-Class': application.mainClass,
'Implementation-Title': rootProject.name,
'Implementation-Version': "${version}"
)
}
from sourceSets.main.output
dependsOn configurations.runtimeClasspath
// Include each JAR dependency
configurations.runtimeClasspath.findAll { it.exists() && it.name.endsWith('.jar') }.each { jarFile ->
def jarName = jarFile.name.replaceAll(/\.jar$/, '')
from(zipTree(jarFile)) {
// Exclude signature-related files
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA'
// Rename license/notice files to avoid conflicts
eachFile { file ->
if (file.path ==~ /META-INF\/(LICENSE|NOTICE)(\..*)?/) {
file.path = "META-INF/licenses-from-${jarName}/${file.name}"
}
}
includeEmptyDirs = false
}
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
javadoc {
options.links("https://www.egothor.org/javadoc/zeroecho/lib")
}

View File

@@ -0,0 +1,309 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.audit.Principal;
import zeroecho.pki.spi.bootstrap.PkiBootstrap;
import zeroecho.pki.util.async.AsyncBus;
/**
* Minimal bootstrap entry point for the {@code pki} module.
*
* <p>
* This class is intentionally limited to process bootstrap and hosting
* concerns:
* </p>
* <ul>
* <li>initializes JUL logging conventions (without leaking secrets),</li>
* <li>installs an uncaught-exception handler,</li>
* <li>composes PKI runtime components using {@link PkiBootstrap},</li>
* <li>hosts the async-bus maintenance loop via periodic
* {@code sweep(...)},</li>
* <li>waits until process termination (Ctrl+C) and performs orderly
* shutdown.</li>
* </ul>
*
* <h2>Async bus sweep</h2>
* <p>
* The async bus requires periodic {@link AsyncBus#sweep(Instant)} calls to:
* expire operations past their deadline and re-synchronize open operations
* after restart (depending on the bus implementation).
* </p>
*
* <h2>Security</h2>
* <p>
* Command-line arguments and configuration values are never logged because they
* can contain sensitive material (paths, tokens, passphrases).
* </p>
*/
@SuppressWarnings("PMD.DoNotUseThreads")
public final class PkiApplication {
private static final Logger LOG = Logger.getLogger(PkiApplication.class.getName());
/**
* System property controlling the async sweep interval in milliseconds.
*
* <p>
* If missing or invalid, a safe default is used.
* </p>
*/
private static final String PROP_ASYNC_SWEEP_INTERVAL_MS = "zeroecho.pki.async.sweepIntervalMs";
/**
* Default async sweep interval used when not configured.
*/
private static final Duration DEFAULT_ASYNC_SWEEP_INTERVAL = Duration.ofSeconds(2L);
/**
* Shutdown grace time for the sweep executor.
*/
private static final Duration SWEEP_SHUTDOWN_GRACE = Duration.ofSeconds(10L);
private PkiApplication() {
throw new AssertionError("No instances.");
}
/**
* Starts the PKI process.
*
* <p>
* Security note: command-line arguments are not logged because they can contain
* sensitive material (paths, tokens, passphrases).
* </p>
*
* @param args command-line arguments (never logged)
*/
public static void main(String[] args) {
Objects.requireNonNull(args, "args");
PkiLogging.configureIfPresent();
PkiLogging.installUncaughtExceptionHandler();
LOG.info("ZeroEcho PKI starting.");
CountDownLatch shutdownLatch = new CountDownLatch(1);
// closed in the shutdown routine
ScheduledExecutorService sweepExecutor = Executors.newSingleThreadScheduledExecutor(new SweepThreadFactory()); // NOPMD
Runtime.getRuntime()
.addShutdownHook(new Thread(new ShutdownHook(sweepExecutor, shutdownLatch), "zeroecho-pki-shutdown"));
try {
AsyncBus<PkiId, Principal, String, Object> asyncBus = PkiBootstrap.openAsyncBus();
Duration sweepInterval = readSweepInterval(DEFAULT_ASYNC_SWEEP_INTERVAL);
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "Async bus sweep enabled; intervalMs={0}", sweepInterval.toMillis());
}
sweepExecutor.scheduleWithFixedDelay(new SweepTask(asyncBus), 0L, sweepInterval.toMillis(),
TimeUnit.MILLISECONDS);
LOG.info("ZeroEcho PKI started.");
// Keep process alive until Ctrl+C (or other shutdown signal).
awaitShutdown(shutdownLatch);
} catch (RuntimeException ex) { // NOPMD
// Do not include user-provided inputs in the message; log the exception object.
LOG.log(Level.SEVERE, "Fatal error during PKI bootstrap.", ex);
throw ex;
}
}
private static void awaitShutdown(CountDownLatch latch) {
try {
latch.await();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Interrupted while awaiting shutdown.", ex);
}
}
}
private static Duration readSweepInterval(Duration defaultValue) {
String raw = System.getProperty(PROP_ASYNC_SWEEP_INTERVAL_MS);
if (raw == null || raw.isBlank()) {
return defaultValue;
}
try {
long ms = Long.parseLong(raw);
if (ms <= 0L) { // NOPMD
return defaultValue;
}
return Duration.ofMillis(ms);
} catch (NumberFormatException ex) {
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Invalid async sweep interval system property; using default.", ex);
}
return defaultValue;
}
}
/**
* Periodic maintenance task for asynchronous PKI infrastructure.
*
* <p>
* {@code SweepTask} represents a resilient, repeatable unit of work that
* invokes time-based maintenance logic on an {@link AsyncBus} instance. It is
* intended to be scheduled at a fixed rate by a background executor and must
* tolerate partial failures without disrupting the surrounding runtime
* environment.
* </p>
*
* <p>
* The task uses the current wall-clock time as a reference for sweep operations
* and deliberately suppresses runtime failures, logging them for diagnostic
* purposes while allowing future executions to proceed.
* </p>
*
* <p>
* This class is internal to the PKI bootstrap and lifecycle management logic
* and is not part of the public API surface.
* </p>
*/
private static final class SweepTask implements Runnable {
private final AsyncBus<PkiId, Principal, String, Object> asyncBus;
private SweepTask(AsyncBus<PkiId, Principal, String, Object> asyncBus) {
this.asyncBus = Objects.requireNonNull(asyncBus, "asyncBus");
}
@Override
public void run() {
Instant now = Instant.now();
try {
asyncBus.sweep(now);
} catch (RuntimeException ex) { // NOPMD
// Sweep must be resilient; log and continue.
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Async sweep failed.", ex);
}
}
}
}
/**
* JVM shutdown hook coordinating termination of asynchronous sweep services.
*
* <p>
* {@code ShutdownHook} is responsible for orchestrating an orderly shutdown of
* background sweep execution during JVM termination. It emits a structured
* shutdown message, initiates executor shutdown, and enforces a bounded grace
* period for task completion.
* </p>
*
* <p>
* The hook guarantees that shutdown coordination always completes by
* decrementing the associated {@link CountDownLatch}, regardless of whether
* shutdown is graceful, forced, or interrupted.
* </p>
*
* <p>
* This class must never throw exceptions or prevent JVM termination. All
* failure modes are handled internally.
* </p>
*/
private static final class ShutdownHook implements Runnable {
private final ScheduledExecutorService sweepExecutor;
private final CountDownLatch latch;
private ShutdownHook(ScheduledExecutorService sweepExecutor, CountDownLatch latch) {
this.sweepExecutor = Objects.requireNonNull(sweepExecutor, "sweepExecutor");
this.latch = Objects.requireNonNull(latch, "latch");
}
@Override
public void run() {
Logger shutdownLogger = Logger.getLogger(PkiApplication.class.getName());
PkiLogging.emitShutdownMessage(shutdownLogger, "ZeroEcho PKI stopping.");
sweepExecutor.shutdown();
try {
boolean ok = sweepExecutor.awaitTermination(SWEEP_SHUTDOWN_GRACE.toMillis(), TimeUnit.MILLISECONDS);
if (!ok) {
sweepExecutor.shutdownNow();
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
sweepExecutor.shutdownNow();
} finally {
latch.countDown();
}
}
}
/**
* Thread factory for asynchronous sweep execution.
*
* <p>
* {@code SweepThreadFactory} creates daemon threads with a stable and
* descriptive naming convention suitable for operational diagnostics and log
* correlation. Threads produced by this factory are intentionally marked as
* daemon threads so that they do not prolong JVM lifetime.
* </p>
*
* <p>
* The factory performs no additional customization such as priority changes or
* uncaught-exception handlers, relying instead on executor-level policies.
* </p>
*/
private static final class SweepThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "zeroecho-pki-async-sweep");
t.setDaemon(true);
return t;
}
}
}

View File

@@ -0,0 +1,178 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* Internal bootstrap utilities for JUL configuration in the {@code pki} module.
*
* <p>
* This helper intentionally avoids logging any potentially sensitive material.
* In particular, it never logs:
* </p>
* <ul>
* <li>command-line arguments,</li>
* <li>key material, seeds, shared secrets,</li>
* <li>private configuration values (tokens, passphrases).</li>
* </ul>
*
* <p>
* Configuration strategy:
* </p>
* <ul>
* <li>If a {@code /zeroecho-pki-logging.properties} resource is present on the
* classpath, it will be loaded via
* {@link LogManager#readConfiguration(InputStream)}.</li>
* <li>If not present, JUL defaults remain in place (minimal bootstrap
* behavior).</li>
* </ul>
*/
final class PkiLogging {
/**
* Optional classpath resource for JUL configuration.
*/
private static final String LOGGING_PROPERTIES_RESOURCE = "/zeroecho-pki-logging.properties";
private static final Logger LOG = Logger.getLogger(PkiLogging.class.getName());
/**
* One-shot guard ensuring JUL configuration is attempted at most once.
*/
private static final AtomicBoolean CONFIGURED = new AtomicBoolean(false);
private PkiLogging() {
throw new AssertionError("No instances.");
}
/**
* Configures JUL from an optional classpath resource, if present.
*
* <p>
* This method is idempotent and safe to call multiple times.
* </p>
*/
/* default */ static void configureIfPresent() {
// Fast-path: already configured
if (!CONFIGURED.compareAndSet(false, true)) {
return;
}
// getResourceAsStream() may return null; try-with-resources handles null safely
try (InputStream is = PkiLogging.class.getResourceAsStream(LOGGING_PROPERTIES_RESOURCE)) {
if (is == null) {
return;
}
LogManager.getLogManager().readConfiguration(is);
LOG.info("JUL configured from classpath resource.");
} catch (IOException ex) {
// Keep message generic; do not leak environment specifics.
LOG.log(Level.WARNING, "Failed to load JUL configuration; continuing with defaults.", ex);
}
}
/**
* Installs a process-wide uncaught exception handler that logs failures via
* JUL.
*
* <p>
* The handler emits a generic message and includes the throwable. It
* deliberately does not serialize additional contextual data that might contain
* secrets.
* </p>
*/
/* default */ static void installUncaughtExceptionHandler() {
UncaughtExceptionHandler handler = (Thread thread, Throwable throwable) -> { // NOPMD
Objects.requireNonNull(thread, "thread");
Objects.requireNonNull(throwable, "throwable");
Logger logger = Logger.getLogger(PkiApplication.class.getName());
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "Uncaught exception in thread: " + thread.getName(), throwable);
}
};
Thread.setDefaultUncaughtExceptionHandler(handler); // NOPMD
}
/**
* Emits a shutdown message in a way that remains visible even during late JVM
* teardown.
*
* <p>
* The primary path is JUL. As a fallback, a constant message is written to
* {@code System.err}. This avoids logging any secrets and improves reliability
* in environments where JUL output may be lost during shutdown.
* </p>
*
* @param logger logger to use for the primary JUL emission
* @param message message to emit; must not contain secrets
* @throws NullPointerException if {@code logger} or {@code message} is
* {@code null}
*/
/* default */ static void emitShutdownMessage(Logger logger, String message) {
Objects.requireNonNull(logger, "logger");
Objects.requireNonNull(message, "message");
// Primary path: JUL
logger.info(message);
// Flush root handlers (covers parent-handler delegation).
Logger root = Logger.getLogger("");
for (java.util.logging.Handler handler : root.getHandlers()) {
try {
handler.flush();
} catch (RuntimeException ignored) { // NOPMD
// Never throw during shutdown
}
}
// Fallback: direct stderr write
try {
System.err.println(message);
System.err.flush();
} catch (RuntimeException ignored) { // NOPMD
// Never throw during shutdown
}
}
}

View File

@@ -0,0 +1,83 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import zeroecho.pki.api.backup.BackupArtifact;
import zeroecho.pki.api.backup.BackupRequest;
import zeroecho.pki.api.backup.BackupVerificationReport;
import zeroecho.pki.api.backup.RestoreReport;
import zeroecho.pki.api.backup.RestoreRequest;
/**
* Backup/restore operations for PKI state.
*
* <p>
* Backups must not implicitly include private keys. Private keys are referenced
* via {@link KeyRef} and may be managed by separate components.
* </p>
*/
public interface BackupService {
/**
* Creates a backup of PKI state.
*
* @param request backup request
* @return backup artifact
* @throws IllegalArgumentException if {@code request} is invalid
* @throws PkiException if backup creation fails
*/
BackupArtifact createBackup(BackupRequest request);
/**
* Restores PKI state from a backup artifact.
*
* @param request restore request
* @return restore report
* @throws IllegalArgumentException if {@code request} is invalid
* @throws PkiException if restore fails
*/
RestoreReport restoreBackup(RestoreRequest request);
/**
* Verifies a backup artifact for structural validity and integrity.
*
* @param artifact backup artifact
* @return verification report
* @throws IllegalArgumentException if {@code artifact} is null
* @throws PkiException if verification fails due to IO/backend
* errors
*/
BackupVerificationReport verifyBackup(BackupArtifact artifact);
}

View File

@@ -0,0 +1,172 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.util.List;
import zeroecho.pki.api.ca.CaCreateCommand;
import zeroecho.pki.api.ca.CaImportCommand;
import zeroecho.pki.api.ca.CaKeyRotationCommand;
import zeroecho.pki.api.ca.CaQuery;
import zeroecho.pki.api.ca.CaRecord;
import zeroecho.pki.api.ca.CaRolloverCommand;
import zeroecho.pki.api.ca.CaState;
import zeroecho.pki.api.ca.IntermediateCertIssueCommand;
import zeroecho.pki.api.ca.IntermediateCreateCommand;
import zeroecho.pki.api.credential.Credential;
/**
* Manages Certificate Authority (CA) entities and their lifecycle.
*
* <p>
* A CA entity represents an administrative unit capable of issuing credentials.
* A CA entity may own multiple CA credentials over time to support
* cross-signing, rollover, and key rotation.
* </p>
*
* <p>
* Private key material is never handled directly by the PKI module; the CA key
* is referenced by {@link KeyRef} and resolved by runtime wiring.
* </p>
*/
public interface CaService {
/**
* Creates a new root CA entity and issues its initial CA credential.
*
* @param command create command defining subject/profile and optional key
* reference
* @return created CA identifier
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if creation fails due to policy, storage, or
* framework backend error
*/
PkiId createRoot(CaCreateCommand command);
/**
* Imports an existing root CA into the PKI inventory.
*
* <p>
* This registers a CA entity, associates it with a {@link KeyRef}, and persists
* the existing CA credential. Import does not automatically imply trust; trust
* anchor selection is a consumer decision.
* </p>
*
* @param command import command including CA credential payload and key
* reference
* @return imported CA identifier
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if import fails (e.g., inconsistent
* metadata, storage failure)
*/
PkiId importRoot(CaImportCommand command);
/**
* Creates a new intermediate CA entity and issues its initial intermediate CA
* credential.
*
* @param command intermediate creation command
* @return created intermediate CA identifier
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if creation fails
*/
PkiId createIntermediate(IntermediateCreateCommand command);
/**
* Issues a new CA credential for an existing intermediate CA entity.
*
* <p>
* This operation enables cross-signing and renewal scenarios.
* </p>
*
* @param command issuance command specifying issuer and subject CA entity
* @return newly issued CA credential
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if issuance fails due to policy or backend
* errors
*/
Credential issueIntermediateCertificate(IntermediateCertIssueCommand command);
/**
* Performs a CA credential rollover while keeping the same key reference.
*
* @param command rollover command
* @return CA identifier (same CA id expected; returned for convenience)
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if rollover fails
*/
PkiId rolloverCaCertificate(CaRolloverCommand command);
/**
* Rotates the CA key reference and issues new corresponding CA credentials.
*
* @param command key rotation command
* @return CA identifier (same CA id expected; returned for convenience)
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if rotation fails
*/
PkiId rotateCaKey(CaKeyRotationCommand command);
/**
* Updates CA operational state.
*
* @param caId CA identifier
* @param state new CA state
* @param reason non-empty operator-readable reason suitable for audit
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if CA does not exist or update fails
*/
void setCaState(PkiId caId, CaState state, String reason);
/**
* Retrieves a CA record.
*
* @param caId CA identifier
* @return CA record
* @throws IllegalArgumentException if {@code caId} is invalid
* @throws PkiException if CA does not exist
*/
CaRecord getCa(PkiId caId);
/**
* Lists CA records matching query constraints.
*
* @param query query constraints
* @return list of CA records
* @throws IllegalArgumentException if {@code query} is invalid
* @throws PkiException if listing fails
*/
List<CaRecord> listCas(CaQuery query);
}

View File

@@ -0,0 +1,123 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.util.List;
import java.util.Optional;
import zeroecho.pki.api.issuance.VerificationPolicy;
import zeroecho.pki.api.request.CertificationRequest;
import zeroecho.pki.api.request.ParsedCertificationRequest;
import zeroecho.pki.api.request.ProofOfPossessionResult;
import zeroecho.pki.api.request.RequestQuery;
import zeroecho.pki.api.request.RequestStorePolicy;
/**
* Processes certification requests (CSR-like objects) into a normalized
* representation.
*
* <p>
* This service provides request fingerprinting, parsing, proof-of-possession
* verification, and optional persistence for correlation and auditing. Request
* transport protocols such as ACME are expected to use this service as the core
* processing layer.
* </p>
*/
public interface CertificationRequestService {
/**
* Computes a stable identifier (fingerprint) for the given request payload.
*
* @param request certification request
* @return stable request identifier
* @throws IllegalArgumentException if {@code request} is null
* @throws PkiException if fingerprinting fails
*/
PkiId fingerprint(CertificationRequest request);
/**
* Parses and normalizes a certification request.
*
* @param request certification request
* @return parsed request
* @throws IllegalArgumentException if {@code request} is null
* @throws PkiException if parsing fails (invalid request,
* unsupported format, backend failure)
*/
ParsedCertificationRequest parse(CertificationRequest request);
/**
* Verifies proof-of-possession (PoP) for the private key corresponding to the
* requested public key.
*
* @param parsed parsed request
* @param policy verification policy
* @return PoP verification result
* @throws IllegalArgumentException if inputs are null
* @throws PkiException if verification fails due to backend failure
*/
ProofOfPossessionResult verifyProofOfPossession(ParsedCertificationRequest parsed, VerificationPolicy policy);
/**
* Stores a parsed request for later correlation and audit.
*
* @param parsed parsed request
* @param policy storage policy
* @return stored request id
* @throws IllegalArgumentException if inputs are null
* @throws PkiException if persistence fails
*/
PkiId store(ParsedCertificationRequest parsed, RequestStorePolicy policy);
/**
* Retrieves a stored request.
*
* @param requestId request id
* @return parsed request if present
* @throws IllegalArgumentException if {@code requestId} is null
* @throws PkiException if retrieval fails
*/
Optional<ParsedCertificationRequest> get(PkiId requestId);
/**
* Searches stored requests.
*
* @param query request query
* @return matching requests
* @throws IllegalArgumentException if {@code query} is null
* @throws PkiException if searching fails
*/
List<ParsedCertificationRequest> search(RequestQuery query);
}

View File

@@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import zeroecho.pki.api.credential.Credential;
import zeroecho.pki.api.credential.CredentialQuery;
import zeroecho.pki.api.credential.CredentialStatus;
/**
* Inventory and reporting service for issued credentials.
*/
public interface CredentialInventoryService {
/**
* Retrieves a credential by id.
*
* @param credentialId credential id
* @return credential if present
* @throws IllegalArgumentException if {@code credentialId} is null
* @throws PkiException if retrieval fails
*/
Optional<Credential> get(PkiId credentialId);
/**
* Finds a credential by issuer CA and serial/unique identifier.
*
* @param issuerCaId issuer CA id
* @param serialOrUniqueId serial/unique id
* @return credential if present
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if lookup fails
*/
Optional<Credential> findByIssuerSerial(PkiId issuerCaId, String serialOrUniqueId);
/**
* Lists credentials bound to the same public key identifier.
*
* @param publicKeyId public key id
* @return credentials
* @throws IllegalArgumentException if {@code publicKeyId} is null
* @throws PkiException if lookup fails
*/
List<Credential> listByPublicKeyId(PkiId publicKeyId);
/**
* Searches credentials by query constraints.
*
* @param query query constraints
* @return matching credentials
* @throws IllegalArgumentException if {@code query} is null
* @throws PkiException if search fails
*/
List<Credential> search(CredentialQuery query);
/**
* Computes credential status at a given time.
*
* @param credentialId credential id
* @param now evaluation time
* @return computed status
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if evaluation fails
*/
CredentialStatus computeStatus(PkiId credentialId, Instant now);
}

View File

@@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Immutable container for an encoded artifact.
*
* <p>
* This type intentionally carries only the {@link Encoding} and raw bytes. It
* does not carry a media type, because DER/PEM/BINARY do not uniquely determine
* the semantic meaning (a DER payload may represent a certificate, CSR, CRL,
* etc.). The semantic meaning is carried by the surrounding API context.
* </p>
*
* <p>
* Security note: implementations must never log the raw bytes in full.
* </p>
*
* @param encoding encoding kind
* @param bytes non-empty payload bytes
*/
public record EncodedObject(Encoding encoding, byte[] bytes) {
/**
* Creates an encoded object.
*
* @param encoding encoding kind
* @param bytes non-empty payload bytes
* @throws IllegalArgumentException if {@code encoding} is null or {@code bytes}
* is null/empty
*/
public EncodedObject {
if (encoding == null) {
throw new IllegalArgumentException("encoding must not be null");
}
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException("bytes must not be null/empty");
}
}
}

View File

@@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Specifies the encoding of a binary artifact payload.
*
* <p>
* The encoding indicates how the {@code bytes} inside {@link EncodedObject}
* should be interpreted. The logical meaning of the payload (certificate vs CSR
* vs CRL vs backup) is defined by the surrounding API context and record types.
* </p>
*/
public enum Encoding {
/**
* ASN.1 Distinguished Encoding Rules (DER).
*
* <p>
* Common for X.509 certificates, CRLs, and PKCS#10 certification requests.
* </p>
*/
DER,
/**
* PEM armored textual representation.
*
* <p>
* Typically base64-wrapped DER with header/footer lines.
* </p>
*/
PEM,
/**
* Raw binary blob without implying ASN.1 DER or PEM semantics.
*
* <p>
* Use for non-ASN.1 frameworks (e.g., COSE/JWS) or container payloads (e.g.,
* backup archives).
* </p>
*/
BINARY
}

View File

@@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Identifier of a credential framework/format handled by the PKI core.
*
* <p>
* Examples: {@code "x509"}, {@code "ssh"}, {@code "cose"}, {@code "jws"}.
* </p>
*
* <p>
* This identifier is used to dispatch operations to a framework backend
* implementation.
* </p>
*
* @param value non-empty format identifier string
*/
public record FormatId(String value) {
/**
* Creates a format identifier.
*
* @param value non-empty format identifier string
* @throws IllegalArgumentException if {@code value} is null or blank
*/
public FormatId {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("value must not be null/blank");
}
}
}

View File

@@ -0,0 +1,122 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import zeroecho.pki.api.revocation.RevokedRecord;
import zeroecho.pki.api.transfer.ExportArtifact;
import zeroecho.pki.api.transfer.ExportFormat;
import zeroecho.pki.api.transfer.ExportQuery;
import zeroecho.pki.api.transfer.ImportPolicy;
/**
* Import and export operations for migration and interoperability.
*
* <p>
* Import does not imply trust; it is a controlled operation governed by policy.
* Import/export is expected to be auditable.
* </p>
*/
public interface ImportExportService {
/**
* Imports an issued credential payload into inventory.
*
* @param formatId credential format id
* @param credential encoded credential payload
* @param policy import policy
* @return imported credential id
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if import fails
*/
PkiId importCredential(FormatId formatId, EncodedObject credential, ImportPolicy policy);
/**
* Imports a CA certificate payload into an existing CA entity's credential set.
*
* @param caId CA entity id
* @param caCertificate CA certificate payload
* @param policy import policy
* @return imported credential id
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if import fails
*/
PkiId importCaCertificate(PkiId caId, EncodedObject caCertificate, ImportPolicy policy);
/**
* Imports a revocation record.
*
* @param record revocation record
* @param policy import policy
* @return imported revocation record id (implementation-defined)
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if import fails
*/
PkiId importRevocation(RevokedRecord record, ImportPolicy policy);
/**
* Exports credentials matching the query constraints in the requested export
* format.
*
* @param query export query
* @param format export format
* @return export artifact
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if export fails
*/
ExportArtifact exportCredentials(ExportQuery query, ExportFormat format);
/**
* Exports revocation records matching the query constraints in the requested
* export format.
*
* @param query export query
* @param format export format
* @return export artifact
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if export fails
*/
ExportArtifact exportRevocations(ExportQuery query, ExportFormat format);
/**
* Exports CA materials for a given CA entity in the requested export format.
*
* @param caId CA entity id
* @param format export format
* @return export artifact
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if export fails
*/
ExportArtifact exportCa(PkiId caId, ExportFormat format);
}

View File

@@ -0,0 +1,108 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import zeroecho.pki.api.credential.CredentialBundle;
import zeroecho.pki.api.issuance.BundleCommand;
import zeroecho.pki.api.issuance.IssueEndEntityCommand;
import zeroecho.pki.api.issuance.ReissueCommand;
import zeroecho.pki.api.issuance.RenewCommand;
import zeroecho.pki.api.issuance.ReplaceCommand;
/**
* Issues, renews, replaces, and reissues credentials, and builds distributable
* bundles.
*
* <p>
* This service is framework-agnostic: concrete credential formats are
* implemented by framework backends. The PKI runtime applies policy and profile
* constraints before calling issuance backends.
* </p>
*/
public interface IssuanceService {
/**
* Issues a new end-entity credential.
*
* @param command issuance command
* @return credential bundle (credential plus supporting artifacts)
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if issuance fails
*/
CredentialBundle issueEndEntity(IssueEndEntityCommand command);
/**
* Renews an existing credential according to policy-defined continuity
* semantics.
*
* @param command renewal command
* @return renewed credential bundle
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if renewal fails
*/
CredentialBundle renew(RenewCommand command);
/**
* Replaces an existing credential (e.g., after compromise or attribute
* changes).
*
* @param command replacement command
* @return replacement credential bundle
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if replacement fails
*/
CredentialBundle replace(ReplaceCommand command);
/**
* Reissues based on a stored issuance record.
*
* @param command reissue command
* @return reissued credential bundle
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if reissue fails
*/
CredentialBundle reissue(ReissueCommand command);
/**
* Builds a distributable bundle for an existing credential using chain
* selection rules.
*
* @param command bundle command
* @return bundle
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if bundle building fails
*/
CredentialBundle buildBundle(BundleCommand command);
}

View File

@@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* References an issuing CA entity.
*
* @param caId identifier of the CA entity acting as issuer
*/
public record IssuerRef(PkiId caId) {
/**
* Creates an issuer reference.
*
* @param caId CA identifier
* @throws IllegalArgumentException if {@code caId} is null
*/
public IssuerRef {
if (caId == null) {
throw new IllegalArgumentException("caId must not be null");
}
}
}

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Opaque reference to private key material.
*
* <p>
* The PKI module never handles private key bytes. A {@link KeyRef} is resolved
* by runtime wiring, e.g., by a crypto component, an HSM adapter, or a remote
* signer. Implementations must treat this reference as sensitive metadata and
* avoid logging it unnecessarily.
* </p>
*
* @param value non-empty key reference token
*/
public record KeyRef(String value) {
/**
* Creates a key reference.
*
* @param value non-empty key reference token
* @throws IllegalArgumentException if {@code value} is null or blank
*/
public KeyRef {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("value must not be null/blank");
}
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Base runtime exception for PKI domain failures.
*
* <p>
* This exception is used to report policy violations, storage failures,
* framework backend errors, and other domain-level problems not representable
* as {@link IllegalArgumentException}.
* </p>
*
* <p>
* Security note: exception messages must not contain secrets (private keys,
* plaintext, shared secrets, or other sensitive cryptographic material).
* </p>
*/
public class PkiException extends RuntimeException {
private static final long serialVersionUID = 759504279718537161L;
/**
* Creates a PKI exception with a message.
*
* @param message non-empty message describing the failure in a non-sensitive
* manner
*/
public PkiException(String message) {
super(requireNonBlank(message, "message"));
}
/**
* Creates a PKI exception with a message and cause.
*
* @param message non-empty message describing the failure in a non-sensitive
* manner
* @param cause underlying cause
*/
public PkiException(String message, Throwable cause) {
super(requireNonBlank(message, "message"), cause);
}
private static String requireNonBlank(String value, String name) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException(name + " must not be null/blank");
}
return value;
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Opaque identifier for PKI-managed entities.
*
* <p>
* Instances of this type are used as stable references for CA entities,
* credentials, certification requests, status objects, backups, publications,
* exports, and policy/audit records.
* </p>
*
* <p>
* The value must be treated as an opaque token and persisted verbatim.
* </p>
*
* @param value non-empty identifier string
*/
public record PkiId(String value) {
/**
* Creates an opaque PKI identifier.
*
* @param value non-empty identifier string
* @throws IllegalArgumentException if {@code value} is null or blank
*/
public PkiId {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("value must not be null/blank");
}
}
@Override
public String toString() {
return value;
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import zeroecho.pki.api.issuance.IssuanceInputs;
import zeroecho.pki.api.policy.PolicyDecision;
import zeroecho.pki.api.policy.PolicyTrace;
import zeroecho.pki.api.revocation.RevocationInputs;
/**
* Policy evaluation and explainability.
*
* <p>
* Policy decisions must be deterministic, auditable, and explainable.
* Implementations are expected to provide traces suitable for operator
* troubleshooting and compliance evidence.
* </p>
*/
public interface PolicyService {
/**
* Evaluates an issuance request against policy and profile constraints.
*
* @param inputs normalized issuance inputs
* @return policy decision
* @throws IllegalArgumentException if {@code inputs} is null
* @throws PkiException if evaluation fails
*/
PolicyDecision evaluateIssuance(IssuanceInputs inputs);
/**
* Evaluates a revocation request against policy constraints.
*
* @param inputs normalized revocation inputs
* @return policy decision
* @throws IllegalArgumentException if {@code inputs} is null
* @throws PkiException if evaluation fails
*/
PolicyDecision evaluateRevocation(RevocationInputs inputs);
/**
* Retrieves a trace explaining a previous decision.
*
* @param decisionId decision id
* @return decision trace
* @throws IllegalArgumentException if {@code decisionId} is null
* @throws PkiException if trace retrieval fails
*/
PolicyTrace explain(PkiId decisionId);
}

View File

@@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.util.List;
import zeroecho.pki.api.profile.CertificateProfile;
import zeroecho.pki.api.profile.ProfileQuery;
/**
* Profile registry for credential issuance.
*
* <p>
* Profiles define required/optional attributes, validity limits, and other
* constraints used by policy and framework mapping. Profiles are referenced by
* id during issuance.
* </p>
*/
public interface ProfileService {
/**
* Registers or updates a profile.
*
* @param profile profile definition
* @throws IllegalArgumentException if {@code profile} is null
* @throws PkiException if registration fails
*/
void register(CertificateProfile profile);
/**
* Retrieves a profile by id.
*
* @param profileId profile id
* @return profile
* @throws IllegalArgumentException if {@code profileId} is null/blank
* @throws PkiException if not found or retrieval fails
*/
CertificateProfile get(String profileId);
/**
* Lists profiles matching query constraints.
*
* @param query query constraints
* @return list of profiles
* @throws IllegalArgumentException if {@code query} is null
* @throws PkiException if listing fails
*/
List<CertificateProfile> list(ProfileQuery query);
/**
* Retires a profile to prevent further issuance.
*
* @param profileId profile id
* @param reason non-empty reason suitable for audit
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if retire fails
*/
void retire(String profileId, String reason);
}

View File

@@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.util.List;
import zeroecho.pki.api.publication.PublicationQuery;
import zeroecho.pki.api.publication.PublicationRecord;
import zeroecho.pki.api.publication.PublicationResult;
import zeroecho.pki.api.publication.PublicationTarget;
/**
* Publication and distribution operations.
*
* <p>
* Publishing is an explicit operation enabling parity with established PKI
* systems. Implementations may publish credentials, CA materials, and status
* objects to configured targets such as filesystem mirrors, LDAP directories,
* HTTP endpoints, or object stores.
* </p>
*/
public interface PublicationService {
/**
* Publishes an issued credential to the specified target.
*
* @param credentialId credential id
* @param target publication target
* @return publication result
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if publication fails
*/
PublicationResult publishCredential(PkiId credentialId, PublicationTarget target);
/**
* Publishes CA materials (e.g., CA certificate sets) for the given CA entity to
* the specified target.
*
* @param caId CA entity id
* @param target publication target
* @return publication result
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if publication fails
*/
PublicationResult publishCaMaterials(PkiId caId, PublicationTarget target);
/**
* Publishes a status object to the specified target.
*
* @param statusObjectId status object id
* @param target publication target
* @return publication result
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if publication fails
*/
PublicationResult publishStatusObject(PkiId statusObjectId, PublicationTarget target);
/**
* Lists publication records matching query constraints.
*
* @param query publication query
* @return publication records
* @throws IllegalArgumentException if {@code query} is invalid
* @throws PkiException if listing fails
*/
List<PublicationRecord> listPublications(PublicationQuery query);
}

View File

@@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.util.List;
import java.util.Optional;
import zeroecho.pki.api.revocation.HoldCommand;
import zeroecho.pki.api.revocation.RevocationQuery;
import zeroecho.pki.api.revocation.RevokeCommand;
import zeroecho.pki.api.revocation.RevokedRecord;
import zeroecho.pki.api.revocation.UnholdCommand;
/**
* Revocation operations and revocation record management.
*/
public interface RevocationService {
/**
* Revokes a credential.
*
* @param command revoke command
* @return revocation record
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if revocation fails
*/
RevokedRecord revoke(RevokeCommand command);
/**
* Places a credential on hold.
*
* @param command hold command
* @return revocation record
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if hold fails
*/
RevokedRecord hold(HoldCommand command);
/**
* Removes a hold from a credential.
*
* @param command unhold command
* @return revocation record
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if unhold fails
*/
RevokedRecord unhold(UnholdCommand command);
/**
* Retrieves revocation record for a credential.
*
* @param credentialId credential id
* @return record if present
* @throws IllegalArgumentException if {@code credentialId} is null
* @throws PkiException if retrieval fails
*/
Optional<RevokedRecord> get(PkiId credentialId);
/**
* Searches revocation records.
*
* @param query query constraints
* @return matching records
* @throws IllegalArgumentException if {@code query} is null
* @throws PkiException if search fails
*/
List<RevokedRecord> search(RevocationQuery query);
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.util.List;
import java.util.Optional;
import zeroecho.pki.api.status.StatusObject;
import zeroecho.pki.api.status.StatusObjectGenerateCommand;
import zeroecho.pki.api.status.StatusObjectQuery;
import zeroecho.pki.api.status.StatusObjectType;
/**
* Status object generation and retrieval.
*
* <p>
* Status objects include CRLs, delta CRLs, OCSP responses, or
* framework-specific revocation lists.
* </p>
*/
public interface StatusObjectService {
/**
* Generates a new status object for an issuer CA.
*
* @param command generation command
* @return generated status object
* @throws IllegalArgumentException if {@code command} is invalid
* @throws PkiException if generation fails
*/
StatusObject generate(StatusObjectGenerateCommand command);
/**
* Retrieves the latest status object of a given type for an issuer CA.
*
* @param issuerCaId issuer CA id
* @param type status object type
* @return latest status object if present
* @throws IllegalArgumentException if inputs are invalid
* @throws PkiException if retrieval fails
*/
Optional<StatusObject> getLatest(PkiId issuerCaId, StatusObjectType type);
/**
* Lists status objects matching query constraints.
*
* @param query query constraints
* @return matching status objects
* @throws IllegalArgumentException if {@code query} is invalid
* @throws PkiException if listing fails
*/
List<StatusObject> list(StatusObjectQuery query);
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
/**
* Framework-agnostic subject identifier.
*
* <p>
* This identifier is used for policy evaluation, inventory queries, and audit
* correlation. Framework backends may map it to a distinguished name (DN), a
* claims subject, or a service identity, depending on the credential framework.
* </p>
*
* @param value non-empty subject reference
*/
public record SubjectRef(String value) {
/**
* Creates a subject reference.
*
* @param value non-empty subject reference
* @throws IllegalArgumentException if {@code value} is null or blank
*/
public SubjectRef {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("value must not be null/blank");
}
}
}

View File

@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api;
import java.time.Instant;
/**
* Validity interval for an issued credential.
*
* <p>
* The PKI core requires {@code notAfter} to be strictly after
* {@code notBefore}. Framework-specific interpretations (inclusive/exclusive)
* are resolved by the framework backend.
* </p>
*
* <p>
* Policy and profile constraints (maximum lifetime, not-before skew) must be
* enforced by the PKI runtime.
* </p>
*
* @param notBefore start of validity interval (inclusive)
* @param notAfter end of validity interval (must be after {@code notBefore})
*/
public record Validity(Instant notBefore, Instant notAfter) {
/**
* Creates a validity interval.
*
* @param notBefore start of validity interval (inclusive)
* @param notAfter end of validity interval
* @throws IllegalArgumentException if inputs are null or the interval is
* invalid
*/
public Validity {
if (notBefore == null || notAfter == null) {
throw new IllegalArgumentException("notBefore/notAfter must not be null");
}
if (!notAfter.isAfter(notBefore)) {
throw new IllegalArgumentException("notAfter must be after notBefore");
}
}
}

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
import java.util.Set;
/**
* Governance hints controlling auditing and exportability of an attribute.
*
* <p>
* This model is intentionally minimal. Implementations may interpret it via a
* richer policy decision point (roles, purposes, tenants), but the presence of
* these hints allows consistent enforcement and auditing.
* </p>
*
* @param auditOnAllow if true, successful accesses should be audited
* @param auditOnDeny if true, denied accesses should be audited
* @param exportTargets allowed export targets
*/
public record AttributeAccessPolicy(boolean auditOnAllow, boolean auditOnDeny,
Set<AttributeExportTarget> exportTargets) {
/**
* Creates an access policy.
*
* @throws IllegalArgumentException if {@code exportTargets} is null
*/
public AttributeAccessPolicy {
if (exportTargets == null) {
throw new IllegalArgumentException("exportTargets must not be null");
}
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
import java.util.List;
import java.util.Optional;
/**
* Registry/catalogue of attribute definitions.
*
* <p>
* The catalogue is the shared vocabulary across credential frameworks.
* Identifiers must never be reused with a different meaning. Definitions should
* be versioned under a controlled process.
* </p>
*/
public interface AttributeCatalogue {
/**
* Finds a definition by id.
*
* @param id attribute id
* @return definition if present
* @throws IllegalArgumentException if {@code id} is null
*/
Optional<AttributeDefinition> find(AttributeId id);
/**
* Lists all known definitions.
*
* @return list of definitions
*/
List<AttributeDefinition> listAll();
}

View File

@@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
/**
* Typed and governed attribute definition used across credential frameworks.
*
* <p>
* The definition includes a stable identifier, type information, documentation
* metadata, and governance hints. Frameworks map universal attributes into
* framework-specific fields and extensions.
* </p>
*
* @param id stable attribute identifier
* @param displayName human-readable name
* @param valueType logical value type
* @param multiValued whether multiple values are allowed
* @param sensitivity sensitivity classification
* @param stability lifecycle maturity
* @param accessPolicy governance hints
* @param meta structured documentation metadata
*/
public record AttributeDefinition(AttributeId id, String displayName, AttributeValueType valueType, boolean multiValued,
AttributeSensitivity sensitivity, AttributeStability stability, AttributeAccessPolicy accessPolicy,
AttributeMeta meta) {
/**
* Creates an attribute definition.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public AttributeDefinition {
if (id == null) {
throw new IllegalArgumentException("id must not be null");
}
if (displayName == null || displayName.isBlank()) {
throw new IllegalArgumentException("displayName must not be null/blank");
}
if (valueType == null || sensitivity == null || stability == null || accessPolicy == null || meta == null) {
throw new IllegalArgumentException("non-null fields must not be null");
}
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
/**
* Named export targets used by governance and publication pipelines.
*/
public enum AttributeExportTarget {
/**
* Export via a programmatic API.
*/
API,
/**
* Export for UI rendering.
*/
UI,
/**
* Export to LDAP directory.
*/
LDAP,
/**
* Export to backups.
*/
BACKUP,
/**
* Export to diagnostics/debugging channels (typically heavily redacted).
*/
DIAGNOSTICS
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
/**
* Stable attribute identifier used by the attribute catalogue.
*
* <p>
* Identifiers should be globally stable (recommended as OIDs under a project or
* enterprise arc). Identifiers must never be reused with a different semantic
* meaning.
* </p>
*
* @param value non-empty identifier string
*/
public record AttributeId(String value) {
/**
* Creates an attribute identifier.
*
* @param value non-empty identifier string
* @throws IllegalArgumentException if {@code value} is null or blank
*/
public AttributeId {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("value must not be null/blank");
}
}
}

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
import java.util.List;
import java.util.Map;
/**
* Structured, human-facing documentation metadata for an attribute definition.
*
* <p>
* This metadata is intended to be rendered in operator tooling, APIs, and UI.
* It must not contain secrets.
* </p>
*
* @param description normative description of meaning and usage
* @param notes additional guidance and security considerations
* @param examples example values (must not contain secrets)
* @param tags classification tags (e.g., "identity", "x509", "san")
* @param extra additional annotations for future extensions
* (non-sensitive)
*/
public record AttributeMeta(String description, List<String> notes, List<String> examples, List<String> tags,
Map<String, String> extra) {
/**
* Creates attribute metadata.
*
* @throws IllegalArgumentException if inputs are invalid or collections/maps
* are null
*/
public AttributeMeta {
if (description == null || description.isBlank()) {
throw new IllegalArgumentException("description must not be null/blank");
}
if (notes == null) {
throw new IllegalArgumentException("notes must not be null");
}
if (examples == null) {
throw new IllegalArgumentException("examples must not be null");
}
if (tags == null) {
throw new IllegalArgumentException("tags must not be null");
}
if (extra == null) {
throw new IllegalArgumentException("extra must not be null");
}
}
}

View File

@@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
/**
* Data sensitivity classification for an attribute.
*
* <p>
* Sensitivity influences default auditing/export/redaction behavior.
* Implementations must ensure that SECRET and SENSITIVE values are not exposed
* to logs or unauthorized channels.
* </p>
*/
public enum AttributeSensitivity {
/**
* Public value; can be disclosed broadly.
*/
PUBLIC,
/**
* Internal operational value; restricted to internal components and operators.
*/
INTERNAL,
/**
* Sensitive value; disclosure may create security or privacy risk.
*/
SENSITIVE,
/**
* Secret value; must not be disclosed outside the strictest trust boundary.
*/
SECRET
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* Immutable set of typed attributes.
*
* <p>
* This is a passive container. Production code is expected to mediate
* read/write/export/derive operations through a governance/enforcement layer
* that performs ACL checks and emits audit events.
* </p>
*/
public interface AttributeSet {
/**
* Returns all attribute identifiers present in this set.
*
* @return set of attribute ids
*/
Set<AttributeId> ids();
/**
* Reads a single-valued attribute.
*
* <p>
* If the attribute is multi-valued, implementations may return empty or one
* chosen value; callers should prefer {@link #getAll(AttributeId)} when
* multi-valued is expected.
* </p>
*
* @param id attribute id
* @return attribute value if present
* @throws IllegalArgumentException if {@code id} is null
*/
Optional<AttributeValue> get(AttributeId id);
/**
* Reads a potentially multi-valued attribute.
*
* @param id attribute id
* @return list of values (empty if absent)
* @throws IllegalArgumentException if {@code id} is null
*/
List<AttributeValue> getAll(AttributeId id);
}

View File

@@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
/**
* Lifecycle maturity of an attribute definition.
*/
public enum AttributeStability {
/**
* Attribute is stable and recommended for general use.
*/
STABLE,
/**
* Attribute is experimental and may change under a controlled evolution
* process.
*/
EXPERIMENTAL,
/**
* Attribute is deprecated and should not be used for new profiles.
*/
DEPRECATED
}

View File

@@ -0,0 +1,131 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
import java.time.Instant;
/**
* Typed attribute value.
*
* <p>
* Values are modeled as a sealed hierarchy for type safety and deterministic
* mapping. Implementations must treat values as potentially sensitive and apply
* redaction where required.
* </p>
*/
public sealed interface AttributeValue permits AttributeValue.StringValue, AttributeValue.BooleanValue,
AttributeValue.IntegerValue, AttributeValue.InstantValue, AttributeValue.BytesValue {
/**
* String value.
*
* @param value string content (may be empty depending on attribute definition)
*/
record StringValue(String value) implements AttributeValue {
/**
* Creates a string value.
*
* @param value string content
* @throws IllegalArgumentException if {@code value} is null
*/
public StringValue {
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
}
}
/**
* Boolean value.
*
* @param value boolean content
*/
record BooleanValue(boolean value) implements AttributeValue {
}
/**
* Integer/long value.
*
* @param value numeric content
*/
record IntegerValue(long value) implements AttributeValue {
}
/**
* Instant value.
*
* @param value timestamp content
*/
record InstantValue(Instant value) implements AttributeValue {
/**
* Creates an instant value.
*
* @param value timestamp
* @throws IllegalArgumentException if {@code value} is null
*/
public InstantValue {
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
}
}
/**
* Byte string value.
*
* <p>
* Byte values should be treated as potentially sensitive. Implementations must
* not log full contents.
* </p>
*
* @param value non-empty byte array
*/
record BytesValue(byte[] value) implements AttributeValue {
/**
* Creates a byte string value.
*
* @param value byte array (non-empty)
* @throws IllegalArgumentException if {@code value} is null or empty
*/
public BytesValue {
if (value == null || value.length == 0) {
throw new IllegalArgumentException("value must not be null/empty");
}
}
}
}

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.attr;
/**
* Declares the logical value type of an attribute.
*
* <p>
* Framework backends map these logical types into framework-specific constructs
* (e.g., X.509 extensions, claims, or other credential fields).
* </p>
*/
public enum AttributeValueType {
/**
* UTF-8 string value.
*/
STRING,
/**
* Boolean value.
*/
BOOLEAN,
/**
* Integer/long value.
*/
INTEGER,
/**
* Timestamp value.
*/
INSTANT,
/**
* Raw byte string.
*/
BYTES,
/**
* Object identifier string.
*/
OID,
/**
* Distinguished Name representation (string form with normalization rules
* defined by profile/policy).
*/
DN,
/**
* GeneralName-like identity (DNS/IP/URI/email/etc.) represented in a canonical
* structured form.
*/
GENERAL_NAME,
/**
* Public key information representation (e.g., SPKI).
*/
KEY_INFO,
/**
* Structured composite value.
*/
STRUCT
}

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Attribute catalogue and attribute-level modeling.
*
* <p>
* This package defines a typed attribute system used across the PKI API and
* independent of any specific credential framework. Attributes are described
* through definitions and metadata and carried in structured containers to
* support safe reuse and deterministic mapping into concrete frameworks (e.g.,
* via a framework attribute mapper SPI).
* </p>
*
* <h2>Typical responsibilities</h2>
* <ul>
* <li>Define attribute identity and value types.</li>
* <li>Provide metadata needed for validation and governance (stability,
* sensitivity, etc.).</li>
* <li>Support export/import targets without binding to a certificate
* format.</li>
* </ul>
*
* <p>
* Access control and governance for attribute access is described in
* {@code zeroecho.pki.api.audit}.
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api.attr;

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
/**
* Action performed on an attribute for access governance.
*/
public enum AccessAction {
/**
* Read an attribute value.
*/
READ,
/**
* Write or modify an attribute value.
*/
WRITE,
/**
* Export attribute value to an external channel (e.g., UI, LDAP, backups).
*/
EXPORT,
/**
* Derive/computed attribute value from other sources.
*/
DERIVE
}

View File

@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.PkiId;
/**
* Context used for attribute ACL checks and audit correlation.
*
* @param principal actor requesting access
* @param purpose declared purpose of access
* @param objectId optional object id being accessed (credential id, request
* id, etc.)
* @param formatId optional format id relevant to the object being accessed
*/
public record AccessContext(Principal principal, Purpose purpose, Optional<PkiId> objectId,
Optional<FormatId> formatId) {
/**
* Creates an access context.
*
* @throws IllegalArgumentException if mandatory inputs are null or optional
* containers are null
*/
public AccessContext {
if (principal == null) {
throw new IllegalArgumentException("principal must not be null");
}
if (purpose == null) {
throw new IllegalArgumentException("purpose must not be null");
}
if (objectId == null) {
throw new IllegalArgumentException("objectId must not be null");
}
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
}
}

View File

@@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
/**
* Decision outcome of an access control check.
*/
public enum AccessDecision {
/**
* Access is allowed.
*/
ALLOW,
/**
* Access is denied.
*/
DENY
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
import zeroecho.pki.api.attr.AttributeDefinition;
/**
* Policy decision point for attribute-level access control.
*
* <p>
* This interface decides whether an attribute action is permitted given the
* attribute definition and the access context. A separate enforcement layer is
* expected to record audit events.
* </p>
*/
@FunctionalInterface
public interface AttributeAccessController {
/**
* Evaluates an access request.
*
* @param definition attribute definition
* @param action access action
* @param context access context
* @return allow/deny decision
* @throws IllegalArgumentException if inputs are null
*/
AccessDecision decide(AttributeDefinition definition, AccessAction action, AccessContext context);
}

View File

@@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
import java.util.Optional;
import zeroecho.pki.api.attr.AttributeCatalogue;
import zeroecho.pki.api.attr.AttributeId;
import zeroecho.pki.api.attr.AttributeSet;
import zeroecho.pki.api.attr.AttributeValue;
/**
* Policy enforcement point for attribute access with mandatory auditing.
*
* <p>
* All attribute read/write/export/derive operations should be performed through
* this service to ensure: (1) consistent ACL evaluation and (2) consistent
* audit event emission.
* </p>
*/
public interface AttributeGovernanceService {
/**
* Reads an attribute value after applying access control.
*
* @param catalogue attribute catalogue used to resolve definitions
* @param set attribute set being accessed
* @param id attribute id
* @param context access context
* @return value if present and access is allowed; empty otherwise
* @throws IllegalArgumentException if inputs are null
*/
Optional<AttributeValue> read(AttributeCatalogue catalogue, AttributeSet set, AttributeId id,
AccessContext context);
/**
* Writes an attribute value after applying access control.
*
* @param catalogue attribute catalogue used to resolve definitions
* @param set attribute set being modified
* @param id attribute id
* @param value value to write
* @param context access context
* @return new attribute set instance containing the updated value
* @throws IllegalArgumentException if inputs are null
*/
AttributeSet write(AttributeCatalogue catalogue, AttributeSet set, AttributeId id, AttributeValue value,
AccessContext context);
/**
* Exports an attribute value after applying access control.
*
* <p>
* Export may imply redaction. The exact redaction rules are
* implementation-defined and should take attribute sensitivity and export
* target into account.
* </p>
*
* @param catalogue attribute catalogue used to resolve definitions
* @param set attribute set being exported from
* @param id attribute id
* @param context access context
* @return exported value if present and allowed; empty otherwise
* @throws IllegalArgumentException if inputs are null
*/
Optional<AttributeValue> export(AttributeCatalogue catalogue, AttributeSet set, AttributeId id,
AccessContext context);
/**
* Derives an attribute value from other inputs after applying access control.
*
* <p>
* Derivation may be used to compute attributes such as fingerprints or
* normalized identity fields.
* </p>
*
* @param catalogue attribute catalogue used to resolve definitions
* @param set attribute set being modified
* @param id attribute id
* @param context access context
* @return new attribute set instance containing the derived value
* (implementation-defined)
* @throws IllegalArgumentException if inputs are null
*/
AttributeSet derive(AttributeCatalogue catalogue, AttributeSet set, AttributeId id, AccessContext context);
}

View File

@@ -0,0 +1,213 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
import java.time.Instant;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.PkiId;
/**
* Auditable event emitted by the PKI core and governance layer.
*
* <p>
* An {@code AuditEvent} is an immutable, structured record describing a
* security-relevant action or outcome. Typical event categories include
* high-level PKI operations (issuance, revocation, publication, backup) and
* attribute-level governance decisions (read/export attempts and outcomes).
* </p>
*
* <h2>Security properties</h2>
* <ul>
* <li><strong>No secrets:</strong> {@link #details()} MUST NOT contain secrets
* (keys, seeds, shared secrets, plaintext, private material, or other sensitive
* cryptographic/internal state). It is intended for non-sensitive metadata only
* (e.g., decision, policy id, reason codes, counters).</li>
* <li><strong>Minimality:</strong> callers should prefer coarse,
* non-identifying fields and avoid excessive detail. If an identifier is
* needed, prefer {@link #objectId()} and a stable, non-secret reference.</li>
* </ul>
*
* <h2>Determinism and ordering</h2>
* <p>
* The static comparator returned by {@link #auditOrder()} defines a
* deterministic order for presentation and query results. It does not imply
* causality; it is purely a stable sort key.
* </p>
*
* <h2>Validation</h2>
* <p>
* This record enforces basic invariants: time is non-null, {@code category} and
* {@code action} are non-blank, {@code principal} and {@code purpose} are
* non-null, and optional containers/maps are non-null.
* </p>
*
* @param time event time (server time), never {@code null}
* @param category non-blank category (e.g., {@code "ISSUANCE"},
* {@code "REVOCATION"}, {@code "ATTRIBUTE_ACCESS"})
* @param action non-blank action string (e.g., {@code "ISSUE_END_ENTITY"},
* {@code "REVOKE"}, {@code "READ"})
* @param principal actor responsible for the event, never {@code null}
* @param purpose declared purpose of the operation/access, never {@code null}
* @param objectId optional subject object id (credential id, request id,
* attribute id, etc.), never {@code null}
* @param formatId optional format id related to the object (e.g., encoding),
* never {@code null}
* @param details additional non-sensitive key/value details, never
* {@code null}
*/
public record AuditEvent(Instant time, String category, String action, Principal principal, Purpose purpose,
Optional<PkiId> objectId, Optional<FormatId> formatId, Map<String, String> details) {
/**
* Creates an audit event and validates record invariants.
*
* <p>
* Note that this constructor does not (and cannot) automatically prove the
* absence of secrets in {@link #details()}. The responsibility to redact and
* constrain details is on the event producer.
* </p>
*
* @throws IllegalArgumentException if {@code time} is {@code null},
* {@code category/action} are blank,
* {@code principal/purpose} are {@code null},
* or any of {@code objectId/formatId/details}
* are {@code null}
*/
public AuditEvent {
if (time == null) {
throw new IllegalArgumentException("time must not be null");
}
if (category == null || category.isBlank()) {
throw new IllegalArgumentException("category must not be null/blank");
}
if (action == null || action.isBlank()) {
throw new IllegalArgumentException("action must not be null/blank");
}
if (principal == null) {
throw new IllegalArgumentException("principal must not be null");
}
if (purpose == null) {
throw new IllegalArgumentException("purpose must not be null");
}
if (objectId == null) {
throw new IllegalArgumentException("objectId must not be null");
}
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
if (details == null) {
throw new IllegalArgumentException("details must not be null");
}
}
/**
* Returns a deterministic comparator for audit events.
*
* <p>
* The comparator orders events by: {@code time}, {@code category},
* {@code action}, {@code principal.type}, {@code principal.name}.
* </p>
*
* <p>
* This ordering is intended for consistent presentation and stable test
* assertions. It is not a security primitive.
* </p>
*
* @return deterministic comparator, never {@code null}
*/
public static Comparator<AuditEvent> auditOrder() {
return Comparator.comparing(AuditEvent::time).thenComparing(AuditEvent::category)
.thenComparing(AuditEvent::action).thenComparing(e -> e.principal().type())
.thenComparing(e -> e.principal().name());
}
/**
* Evaluates whether this event matches a query constraint set.
*
* <p>
* Matching semantics:
* </p>
* <ul>
* <li>If {@code query.category} is present, it must equal
* {@link #category()}.</li>
* <li>If {@code query.action} is present, it must equal {@link #action()}.</li>
* <li>If {@code query.after} is present, event time must be {@code >= after}
* (implemented as {@code !time.isBefore(after)}).</li>
* <li>If {@code query.before} is present, event time must be {@code <= before}
* (implemented as {@code !time.isAfter(before)}).</li>
* <li>If {@code query.objectId} is present, it must equal {@link #objectId()}
* (empty optional does not match).</li>
* <li>If {@code query.principalName} is present, it must equal
* {@link #principal()}.{@code name()}.</li>
* </ul>
*
* <p>
* This method is side-effect free and deterministic.
* </p>
*
* @param query query constraints, must not be {@code null}
* @return {@code true} if this event matches all present constraints;
* {@code false} otherwise
* @throws NullPointerException if {@code query} is {@code null}
*/
public boolean matches(AuditQuery query) {
Objects.requireNonNull(query, "query");
if (query.category().isPresent() && !query.category().get().equals(category())) {
return false;
}
if (query.action().isPresent() && !query.action().get().equals(action())) {
return false;
}
if (query.after().isPresent() && time().isBefore(query.after().get())) {
return false;
}
if (query.before().isPresent() && time().isAfter(query.before().get())) {
return false;
}
if (query.objectId().isPresent() && !query.objectId().get().equals(objectId().orElse(null))) {
return false;
}
if (query.principalName().isPresent() && !query.principalName().get().equals(principal().name())) { // NOPMD
return false;
}
return true;
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
import java.time.Instant;
import java.util.Optional;
import zeroecho.pki.api.PkiId;
/**
* Query constraints for searching audit events.
*
* @param category optional category filter
* @param action optional action filter
* @param after optional lower bound for event time
* @param before optional upper bound for event time
* @param objectId optional object id filter
* @param principalName optional principal name filter
*/
public record AuditQuery(Optional<String> category, Optional<String> action, Optional<Instant> after,
Optional<Instant> before, Optional<PkiId> objectId, Optional<String> principalName) {
/**
* Creates an audit query.
*
* @throws IllegalArgumentException if any optional container is null
*/
public AuditQuery {
if (category == null || action == null || after == null || before == null || objectId == null
|| principalName == null) {
throw new IllegalArgumentException("optional fields must not be null");
}
}
}

View File

@@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
import java.util.List;
import java.util.Optional;
import zeroecho.pki.api.PkiId;
/**
* Records and queries audit events for PKI operations and attribute governance.
*
* <p>
* Implementations must ensure sensitive data is never stored or logged in clear
* text.
* </p>
*/
public interface AuditService {
/**
* Records an audit event.
*
* @param event audit event
* @throws IllegalArgumentException if {@code event} is null
* @throws RuntimeException if recording fails (implementation-defined)
*/
void record(AuditEvent event);
/**
* Searches audit events by query constraints.
*
* @param query query constraints
* @return matching audit events
* @throws IllegalArgumentException if {@code query} is null
* @throws RuntimeException if search fails
*/
List<AuditEvent> search(AuditQuery query);
/**
* Retrieves an audit event by id if the implementation assigns stable ids.
*
* @param eventId event id
* @return audit event if present
* @throws IllegalArgumentException if {@code eventId} is null
* @throws RuntimeException if retrieval fails
*/
Optional<AuditEvent> get(PkiId eventId);
}

View File

@@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
/**
* Identifies an actor performing an operation or requesting access.
*
* <p>
* A principal may represent a human user, service account, subsystem component,
* or a scheduled job.
* </p>
*
* @param type principal type (e.g., "USER", "SERVICE", "COMPONENT")
* @param name principal name/identifier
*/
public record Principal(String type, String name) {
/**
* Creates a principal.
*
* @throws IllegalArgumentException if inputs are null/blank
*/
public Principal {
if (type == null || type.isBlank()) {
throw new IllegalArgumentException("type must not be null/blank");
}
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("name must not be null/blank");
}
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.audit;
/**
* Declares the purpose of an operation/access for governance and auditing.
*
* <p>
* Examples: {@code ISSUANCE}, {@code VALIDATION}, {@code UI_RENDER},
* {@code BACKUP_EXPORT}, {@code LDAP_PUBLISH}.
* </p>
*
* @param value non-empty purpose string
*/
public record Purpose(String value) {
/**
* Creates a purpose.
*
* @throws IllegalArgumentException if {@code value} is null/blank
*/
public Purpose {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("value must not be null/blank");
}
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Audit and governance API.
*
* <p>
* This package defines audit events, principals, purposes, queries, and
* attribute-access governance abstractions used to support compliance and
* operational forensics.
* </p>
*
* <h2>Security</h2>
* <ul>
* <li>Audit events MUST NOT contain private keys, shared secrets, or plaintext
* sensitive content.</li>
* <li>Audit records should be structured and stable for long-term retention and
* analysis.</li>
* </ul>
*
* <p>
* The persistence and routing of audit events is an SPI concern (e.g.,
* {@code zeroecho.pki.spi.AuditSink}).
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api.audit;

View File

@@ -0,0 +1,69 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.backup;
import zeroecho.pki.api.BackupService;
import zeroecho.pki.api.EncodedObject;
import zeroecho.pki.api.Encoding;
import zeroecho.pki.api.PkiId;
/**
* Opaque backup artifact produced by {@link BackupService}.
*
* <p>
* The payload is typically {@link Encoding#BINARY}. The internal structure is
* implementation-defined (e.g., tar/zip-like). Consumers should treat it as
* opaque.
* </p>
*
* @param backupId backup identifier
* @param payload backup payload bytes
*/
public record BackupArtifact(PkiId backupId, EncodedObject payload) {
/**
* Creates a backup artifact.
*
* @throws IllegalArgumentException if inputs are null
*/
public BackupArtifact {
if (backupId == null) {
throw new IllegalArgumentException("backupId must not be null");
}
if (payload == null) {
throw new IllegalArgumentException("payload must not be null");
}
}
}

View File

@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.backup;
import zeroecho.pki.api.KeyRef;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Requests creation of a PKI backup.
*
* <p>
* Backups are expected to contain PKI state (CAs, credentials, requests,
* revocations, profiles, publication records, policy traces). Private keys are
* excluded and referenced via {@link KeyRef}.
* </p>
*
* @param label operator-provided label for human identification
* @param attributes optional backup metadata (may be empty but not null)
*/
public record BackupRequest(String label, AttributeSet attributes) {
/**
* Creates a backup request.
*
* @throws IllegalArgumentException if {@code label} is null/blank or
* {@code attributes} is null
*/
public BackupRequest {
if (label == null || label.isBlank()) {
throw new IllegalArgumentException("label must not be null/blank");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.backup;
import java.util.List;
/**
* Verification results for a backup artifact.
*
* @param valid true if the artifact is structurally valid and integrity checks
* passed
* @param issues list of issues found (non-sensitive)
*/
public record BackupVerificationReport(boolean valid, List<String> issues) {
/**
* Creates a backup verification report.
*
* @throws IllegalArgumentException if {@code issues} is null
*/
public BackupVerificationReport {
if (issues == null) {
throw new IllegalArgumentException("issues must not be null");
}
}
}

View File

@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.backup;
import java.util.List;
import zeroecho.pki.api.PkiId;
/**
* Result report for a restore operation.
*
* @param restoreId restore identifier
* @param success true if restore completed successfully
* @param warnings operator-readable warnings (non-sensitive)
* @param errors operator-readable errors (non-sensitive)
*/
public record RestoreReport(PkiId restoreId, boolean success, List<String> warnings, List<String> errors) {
/**
* Creates a restore report.
*
* @throws IllegalArgumentException if {@code restoreId} is null or lists are
* null
*/
public RestoreReport {
if (restoreId == null) {
throw new IllegalArgumentException("restoreId must not be null");
}
if (warnings == null) {
throw new IllegalArgumentException("warnings must not be null");
}
if (errors == null) {
throw new IllegalArgumentException("errors must not be null");
}
}
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.backup;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Requests restore of PKI state from a backup artifact.
*
* @param artifact backup artifact
* @param attributes optional restore hints (may be empty but not null)
*/
public record RestoreRequest(BackupArtifact artifact, AttributeSet attributes) {
/**
* Creates a restore request.
*
* @throws IllegalArgumentException if inputs are null
*/
public RestoreRequest {
if (artifact == null) {
throw new IllegalArgumentException("artifact must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Backup and restore domain model.
*
* <p>
* This package provides request/response and artifact model types used for
* backing up and restoring PKI state. The intent is to support offline escrow,
* migration, disaster recovery, and integrity verification workflows.
* </p>
*
* <h2>Scope</h2>
* <ul>
* <li>Backup artifacts describe exported PKI state suitable for durable
* storage.</li>
* <li>Verification reports provide evidence that backups are structurally valid
* and complete.</li>
* <li>Restore requests and reports model controlled restoration
* operations.</li>
* </ul>
*
* <p>
* Concrete serialization formats and transport mechanisms are handled by
* services and the transfer layer.
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api.backup;

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.KeyRef;
import zeroecho.pki.api.SubjectRef;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to create a new root CA entity and issue its initial CA credential.
*
* <p>
* If {@code keyRef} is absent, the runtime may generate a new key pair
* depending on policy and runtime wiring. This command carries universal
* {@code attributes} used by policy and mapping.
* </p>
*
* @param formatId target credential format
* @param subjectRef normalized CA subject reference
* @param profileId profile id governing issuance and mapping
* @param keyRef optional existing key reference; empty requests key
* generation
* @param attributes universal attributes (may be empty but not null)
*/
public record CaCreateCommand(FormatId formatId, SubjectRef subjectRef, String profileId, Optional<KeyRef> keyRef,
AttributeSet attributes) {
/**
* Creates a CA create command.
*
* @throws IllegalArgumentException if inputs are invalid or optional container
* is null
*/
public CaCreateCommand {
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
if (subjectRef == null) {
throw new IllegalArgumentException("subjectRef must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (keyRef == null) {
throw new IllegalArgumentException("keyRef must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import zeroecho.pki.api.EncodedObject;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.KeyRef;
import zeroecho.pki.api.SubjectRef;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to import an existing root CA credential into PKI inventory.
*
* <p>
* This operation registers a CA entity and associates it with an externally
* managed key reference.
* </p>
*
* @param formatId credential format id
* @param subjectRef normalized CA subject reference
* @param profileId profile id for mapping/constraints
* @param keyRef reference to private key material
* @param existingCaCredential existing CA credential payload (certificate-like)
* @param attributes universal attributes (may be empty but not null)
*/
public record CaImportCommand(FormatId formatId, SubjectRef subjectRef, String profileId, KeyRef keyRef,
EncodedObject existingCaCredential, AttributeSet attributes) {
/**
* Creates a CA import command.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public CaImportCommand {
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
if (subjectRef == null) {
throw new IllegalArgumentException("subjectRef must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (keyRef == null) {
throw new IllegalArgumentException("keyRef must not be null");
}
if (existingCaCredential == null) {
throw new IllegalArgumentException("existingCaCredential must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,83 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.Optional;
import zeroecho.pki.api.KeyRef;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to rotate a CA key reference and issue new corresponding CA
* credentials.
*
* <p>
* Key rotation changes the underlying key material. Historical key references
* and credentials must remain discoverable for audit and validation of
* previously issued credentials.
* </p>
*
* @param caId CA entity id
* @param newKeyRef optional new key reference; empty requests key generation
* via runtime wiring
* @param issuerCaId optional issuer CA id (required for intermediate rotation;
* empty for root depending on policy)
* @param attributes universal attributes (may be empty but not null)
*/
public record CaKeyRotationCommand(PkiId caId, Optional<KeyRef> newKeyRef, Optional<PkiId> issuerCaId,
AttributeSet attributes) {
/**
* Creates a CA key rotation command.
*
* @throws IllegalArgumentException if inputs are invalid or optional containers
* are null
*/
public CaKeyRotationCommand {
if (caId == null) {
throw new IllegalArgumentException("caId must not be null");
}
if (newKeyRef == null) {
throw new IllegalArgumentException("newKeyRef must not be null");
}
if (issuerCaId == null) {
throw new IllegalArgumentException("issuerCaId must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
/**
* Classifies CA entity type.
*/
public enum CaKind {
/**
* Root CA (initial credential is typically self-issued).
*/
ROOT,
/**
* Intermediate CA (issued by another CA).
*/
INTERMEDIATE
}

View File

@@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.SubjectRef;
/**
* Query constraints for listing CA entities.
*
* @param kind optional CA kind filter
* @param state optional state filter
* @param formatId optional framework filter (implementation-defined; may map
* to CA credential format)
* @param subjectRef optional subject filter
*/
public record CaQuery(Optional<CaKind> kind, Optional<CaState> state, Optional<FormatId> formatId,
Optional<SubjectRef> subjectRef) {
/**
* Creates a CA query.
*
* @throws IllegalArgumentException if any optional container is null
*/
public CaQuery {
if (kind == null || state == null || formatId == null || subjectRef == null) {
throw new IllegalArgumentException("optional fields must not be null");
}
}
}

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.List;
import zeroecho.pki.api.KeyRef;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.SubjectRef;
import zeroecho.pki.api.credential.Credential;
/**
* Represents a CA entity and its issued CA credentials.
*
* <p>
* A CA entity may have multiple CA credentials to support:
* </p>
* <ul>
* <li>cross-signing (multiple issuers for the same subject key),</li>
* <li>credential rollover (new CA credential with the same key),</li>
* <li>key rotation (new CA key with a new set of credentials).</li>
* </ul>
*
* @param caId CA identifier
* @param kind CA kind (root or intermediate)
* @param state operational state
* @param issuerKeyRef key reference used for issuing operations (private key
* reference)
* @param subjectRef normalized subject reference
* @param caCredentials CA credentials currently associated with the entity
* (historical and active)
*/
public record CaRecord(PkiId caId, CaKind kind, CaState state, KeyRef issuerKeyRef, SubjectRef subjectRef,
List<Credential> caCredentials) {
/**
* Creates a CA record.
*
* @throws IllegalArgumentException if inputs are null
*/
public CaRecord {
if (caId == null) {
throw new IllegalArgumentException("caId must not be null");
}
if (kind == null) {
throw new IllegalArgumentException("kind must not be null");
}
if (state == null) {
throw new IllegalArgumentException("state must not be null");
}
if (issuerKeyRef == null) {
throw new IllegalArgumentException("issuerKeyRef must not be null");
}
if (subjectRef == null) {
throw new IllegalArgumentException("subjectRef must not be null");
}
if (caCredentials == null) {
throw new IllegalArgumentException("caCredentials must not be null");
}
}
}

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.Optional;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.Validity;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to roll over a CA credential while keeping the same key reference.
*
* <p>
* Rollover issues a new CA credential for the CA entity without changing the
* underlying key material. Historical credentials remain accessible for audit
* and chain selection.
* </p>
*
* @param caId CA entity id
* @param issuerCaId optional issuer CA id (empty for self-issued root
* rollover where applicable)
* @param requestedValidity optional requested validity
* @param attributes universal attributes (may be empty but not null)
*/
public record CaRolloverCommand(PkiId caId, Optional<PkiId> issuerCaId, Optional<Validity> requestedValidity,
AttributeSet attributes) {
/**
* Creates a CA rollover command.
*
* @throws IllegalArgumentException if inputs are invalid or optional containers
* are null
*/
public CaRolloverCommand {
if (caId == null) {
throw new IllegalArgumentException("caId must not be null");
}
if (issuerCaId == null) {
throw new IllegalArgumentException("issuerCaId must not be null");
}
if (requestedValidity == null) {
throw new IllegalArgumentException("requestedValidity must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
/**
* Operational state of a CA entity.
*/
public enum CaState {
/**
* CA is active and may issue new credentials according to policy.
*/
ACTIVE,
/**
* CA is retired and must not issue new credentials.
*
* <p>
* Historical credentials remain available for validation and audit until they
* expire or are revoked.
* </p>
*/
RETIRED,
/**
* CA is compromised and must not be used for issuance.
*
* <p>
* Operators should perform incident response, publish updated status objects,
* and rotate trust anchors.
* </p>
*/
COMPROMISED,
/**
* CA is administratively disabled.
*
* <p>
* This state is distinct from retirement and may be reversible.
* </p>
*/
DISABLED
}

View File

@@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.Validity;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to issue a new CA credential for an existing intermediate CA entity.
*
* <p>
* This command supports cross-signing (issuing with a different issuer CA) and
* renewal scenarios.
* </p>
*
* @param formatId credential format id
* @param issuerCaId issuer CA entity id
* @param subjectCaId subject CA entity id (the intermediate being
* certified)
* @param profileId profile id governing issuance
* @param requestedValidity optional requested validity (policy may
* override/deny)
* @param attributes universal attributes (may be empty but not null)
*/
public record IntermediateCertIssueCommand(FormatId formatId, PkiId issuerCaId, PkiId subjectCaId, String profileId,
Optional<Validity> requestedValidity, AttributeSet attributes) {
/**
* Creates an intermediate CA credential issuance command.
*
* @throws IllegalArgumentException if inputs are invalid or optional container
* is null
*/
public IntermediateCertIssueCommand {
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
if (issuerCaId == null) {
throw new IllegalArgumentException("issuerCaId must not be null");
}
if (subjectCaId == null) {
throw new IllegalArgumentException("subjectCaId must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (requestedValidity == null) {
throw new IllegalArgumentException("requestedValidity must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.ca;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.KeyRef;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.SubjectRef;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to create a new intermediate CA entity and issue its initial CA
* credential.
*
* @param formatId credential format id
* @param issuerCaId issuer CA entity id
* @param subjectRef normalized subject reference for the intermediate
* @param profileId profile id governing issuance
* @param keyRef optional existing key reference; empty requests key
* generation
* @param attributes universal attributes (may be empty but not null)
*/
public record IntermediateCreateCommand(FormatId formatId, PkiId issuerCaId, SubjectRef subjectRef, String profileId,
Optional<KeyRef> keyRef, AttributeSet attributes) {
/**
* Creates an intermediate create command.
*
* @throws IllegalArgumentException if inputs are invalid or optional container
* is null
*/
public IntermediateCreateCommand {
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
if (issuerCaId == null) {
throw new IllegalArgumentException("issuerCaId must not be null");
}
if (subjectRef == null) {
throw new IllegalArgumentException("subjectRef must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (keyRef == null) {
throw new IllegalArgumentException("keyRef must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Certificate Authority (CA) domain model.
*
* <p>
* This package contains CA records, lifecycle state, CA kinds, and CA-related
* commands and queries. It models root and intermediate CA management,
* including creation, import, rollover, and key rotation operations.
* </p>
*
* <h2>Responsibilities</h2>
* <ul>
* <li>Represent CA identity and state through records and enums.</li>
* <li>Define CA management commands used by
* {@link zeroecho.pki.api.CaService}.</li>
* <li>Support intermediate CA creation and intermediate certificate
* issuance.</li>
* </ul>
*
* <p>
* Concrete certificate framework specifics are delegated to framework
* integrations.
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api.ca;

View File

@@ -0,0 +1,117 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.credential;
import zeroecho.pki.api.EncodedObject;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.IssuerRef;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.SubjectRef;
import zeroecho.pki.api.Validity;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Issued credential with mandatory core metadata and universal attributes.
*
* <p>
* The PKI core does not assume X.509 semantics. The {@code serialOrUniqueId}
* field maps to X.509 serial numbers when applicable, but can represent another
* framework's unique identifier.
* </p>
*
* <p>
* The {@code publicKeyId} is intended to group multiple credentials for the
* same key (e.g., cross-signing, migrations, or parallel classical/PQC chains).
* </p>
*
* @param credentialId stable identifier for the credential (typically a
* fingerprint of encoded bytes)
* @param formatId framework identifier
* @param issuerRef issuing CA reference
* @param subjectRef normalized subject reference
* @param validity validity interval
* @param serialOrUniqueId framework-specific unique identifier (serial for
* X.509)
* @param publicKeyId stable identifier derived from the subject public key
* @param profileId profile governing issuance
* @param status inventory status
* @param encoded encoded credential bytes
* @param attributes universal attribute set
*/
public record Credential(PkiId credentialId, FormatId formatId, IssuerRef issuerRef, SubjectRef subjectRef,
Validity validity, String serialOrUniqueId, PkiId publicKeyId, String profileId, CredentialStatus status,
EncodedObject encoded, AttributeSet attributes) {
/**
* Creates a credential record.
*
* @throws IllegalArgumentException if mandatory inputs are invalid
*/
public Credential {
if (credentialId == null) {
throw new IllegalArgumentException("credentialId must not be null");
}
if (formatId == null) {
throw new IllegalArgumentException("formatId must not be null");
}
if (issuerRef == null) {
throw new IllegalArgumentException("issuerRef must not be null");
}
if (subjectRef == null) {
throw new IllegalArgumentException("subjectRef must not be null");
}
if (validity == null) {
throw new IllegalArgumentException("validity must not be null");
}
if (serialOrUniqueId == null || serialOrUniqueId.isBlank()) {
throw new IllegalArgumentException("serialOrUniqueId must not be null/blank");
}
if (publicKeyId == null) {
throw new IllegalArgumentException("publicKeyId must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (status == null) {
throw new IllegalArgumentException("status must not be null");
}
if (encoded == null) {
throw new IllegalArgumentException("encoded must not be null");
}
if (attributes == null) {
throw new IllegalArgumentException("attributes must not be null");
}
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.credential;
import java.util.List;
import zeroecho.pki.api.EncodedObject;
/**
* Bundle of a primary credential and supporting objects.
*
* <p>
* Supporting objects enable distribution and validation. For X.509 these are
* typically chain certificates. Frameworks may define additional supporting
* artifacts.
* </p>
*
* @param credential primary credential
* @param supportingObjects supporting artifacts (framework-defined ordering)
*/
public record CredentialBundle(Credential credential, List<EncodedObject> supportingObjects) {
/**
* Creates a bundle.
*
* @param credential primary credential
* @param supportingObjects supporting artifacts (non-null, may be empty)
* @throws IllegalArgumentException if inputs are null
*/
public CredentialBundle {
if (credential == null) {
throw new IllegalArgumentException("credential must not be null");
}
if (supportingObjects == null) {
throw new IllegalArgumentException("supportingObjects must not be null");
}
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.credential;
import java.time.Instant;
import java.util.Optional;
import zeroecho.pki.api.FormatId;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.SubjectRef;
/**
* Query constraints for searching credentials in inventory.
*
* @param formatId optional framework filter
* @param issuerCaId optional issuer CA filter
* @param subjectRef optional subject filter
* @param profileId optional profile filter
* @param status optional status filter
* @param publicKeyId optional public key grouping filter
* @param validAt optional evaluation time for validity-based filtering
*/
public record CredentialQuery(Optional<FormatId> formatId, Optional<PkiId> issuerCaId, Optional<SubjectRef> subjectRef,
Optional<String> profileId, Optional<CredentialStatus> status, Optional<PkiId> publicKeyId,
Optional<Instant> validAt) {
/**
* Creates a credential query.
*
* @throws IllegalArgumentException if any optional container is null
*/
public CredentialQuery {
if (formatId == null || issuerCaId == null || subjectRef == null || profileId == null || status == null
|| publicKeyId == null || validAt == null) {
throw new IllegalArgumentException("optional fields must not be null");
}
}
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.credential;
/**
* Status of a credential as tracked by PKI inventory.
*
* <p>
* Status may be computed from validity and revocation state or stored directly
* depending on implementation.
* </p>
*/
public enum CredentialStatus {
/**
* Credential is issued and not revoked. Validity may still expire later.
*/
ISSUED,
/**
* Credential is revoked.
*/
REVOKED,
/**
* Credential validity interval has ended.
*/
EXPIRED
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Credential inventory domain model.
*
* <p>
* This package defines the model types representing issued credentials and
* their inventory view, including status tracking and query objects. It is used
* by {@link zeroecho.pki.api.CredentialInventoryService}.
* </p>
*
* <h2>Notes</h2>
* <ul>
* <li>Credentials are treated as immutable artifacts once issued.</li>
* <li>Status values capture the operational lifecycle (e.g., issued, expired,
* revoked, on hold).</li>
* </ul>
*
* @since 1.0
*/
package zeroecho.pki.api.credential;

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import java.util.Optional;
import zeroecho.pki.api.PkiId;
/**
* Command to build a distributable bundle for an existing credential.
*
* <p>
* Bundles are constructed using chain selection rules, trust anchor selection,
* and optional compatibility profiles. This is especially relevant for
* cross-signing and migration scenarios.
* </p>
*
* @param credentialId credential id
* @param preferredTrustAnchorId optional preferred trust anchor id
* (implementation-defined)
* @param compatibilityProfileId optional compatibility profile id influencing
* chain selection
*/
public record BundleCommand(PkiId credentialId, Optional<PkiId> preferredTrustAnchorId,
Optional<String> compatibilityProfileId) {
/**
* Creates a bundle command.
*
* @throws IllegalArgumentException if inputs are invalid or optional containers
* are null
*/
public BundleCommand {
if (credentialId == null) {
throw new IllegalArgumentException("credentialId must not be null");
}
if (preferredTrustAnchorId == null) {
throw new IllegalArgumentException("preferredTrustAnchorId must not be null");
}
if (compatibilityProfileId == null) {
throw new IllegalArgumentException("compatibilityProfileId must not be null");
}
}
}

View File

@@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.attr.AttributeSet;
import zeroecho.pki.api.request.ParsedCertificationRequest;
/**
* Normalized inputs for issuance policy evaluation.
*
* @param issuerCaId issuer CA entity id
* @param request parsed certification request
* @param profileId profile id selected for issuance
* @param requestedOverrides user-requested overrides (may be empty but not
* null)
*/
public record IssuanceInputs(PkiId issuerCaId, ParsedCertificationRequest request, String profileId,
AttributeSet requestedOverrides) {
/**
* Creates issuance inputs for policy evaluation.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public IssuanceInputs {
if (issuerCaId == null) {
throw new IllegalArgumentException("issuerCaId must not be null");
}
if (request == null) {
throw new IllegalArgumentException("request must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (requestedOverrides == null) {
throw new IllegalArgumentException("requestedOverrides must not be null");
}
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import java.util.Optional;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.Validity;
import zeroecho.pki.api.attr.AttributeSet;
import zeroecho.pki.api.request.ParsedCertificationRequest;
/**
* Command to issue an end-entity credential from a parsed certification
* request.
*
* @param issuerCaId issuer CA entity id
* @param request parsed certification request
* @param profileId profile id governing issuance
* @param validityOverride optional requested validity override
* (policy-validated)
* @param overrides additional universal attribute overrides
* (policy-validated; may be empty but not null)
*/
public record IssueEndEntityCommand(PkiId issuerCaId, ParsedCertificationRequest request, String profileId,
Optional<Validity> validityOverride, AttributeSet overrides) {
/**
* Creates an issuance command.
*
* @throws IllegalArgumentException if inputs are invalid or optional container
* is null
*/
public IssueEndEntityCommand {
if (issuerCaId == null) {
throw new IllegalArgumentException("issuerCaId must not be null");
}
if (request == null) {
throw new IllegalArgumentException("request must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (validityOverride == null) {
throw new IllegalArgumentException("validityOverride must not be null");
}
if (overrides == null) {
throw new IllegalArgumentException("overrides must not be null");
}
}
}

View File

@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to reissue based on a stored issuance record.
*
* <p>
* The meaning of "issuance record" is implementation-defined (it may be derived
* from audit/store metadata). Reissue is useful for reproducing issuance under
* controlled changes.
* </p>
*
* @param issuanceRecordId issuance record id
* @param overrides universal attribute overrides (policy-validated; may
* be empty but not null)
*/
public record ReissueCommand(PkiId issuanceRecordId, AttributeSet overrides) {
/**
* Creates a reissue command.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public ReissueCommand {
if (issuanceRecordId == null) {
throw new IllegalArgumentException("issuanceRecordId must not be null");
}
if (overrides == null) {
throw new IllegalArgumentException("overrides must not be null");
}
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import java.util.Optional;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.Validity;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Command to renew an existing credential.
*
* <p>
* Renewal typically retains continuity (same identity and key constraints)
* under policy-defined semantics. The implementation decides what "renew" means
* for a given framework and profile.
* </p>
*
* @param existingCredentialId credential id to renew
* @param validityOverride optional validity override (policy-validated)
* @param overrides universal attribute overrides (policy-validated;
* may be empty but not null)
*/
public record RenewCommand(PkiId existingCredentialId, Optional<Validity> validityOverride, AttributeSet overrides) {
/**
* Creates a renewal command.
*
* @throws IllegalArgumentException if inputs are invalid or optional container
* is null
*/
public RenewCommand {
if (existingCredentialId == null) {
throw new IllegalArgumentException("existingCredentialId must not be null");
}
if (validityOverride == null) {
throw new IllegalArgumentException("validityOverride must not be null");
}
if (overrides == null) {
throw new IllegalArgumentException("overrides must not be null");
}
}
}

View File

@@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.attr.AttributeSet;
import zeroecho.pki.api.request.ParsedCertificationRequest;
/**
* Command to replace an existing credential.
*
* <p>
* Replacement is used for scenarios such as compromise or identity attribute
* changes. Policy determines whether replacement is permitted and what
* continuity constraints apply.
* </p>
*
* @param existingCredentialId existing credential id
* @param newRequest new parsed request for the replacement credential
* @param profileId profile id governing issuance
* @param overrides universal attribute overrides (policy-validated;
* may be empty but not null)
*/
public record ReplaceCommand(PkiId existingCredentialId, ParsedCertificationRequest newRequest, String profileId,
AttributeSet overrides) {
/**
* Creates a replacement command.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public ReplaceCommand {
if (existingCredentialId == null) {
throw new IllegalArgumentException("existingCredentialId must not be null");
}
if (newRequest == null) {
throw new IllegalArgumentException("newRequest must not be null");
}
if (profileId == null || profileId.isBlank()) {
throw new IllegalArgumentException("profileId must not be null/blank");
}
if (overrides == null) {
throw new IllegalArgumentException("overrides must not be null");
}
}
}

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.issuance;
import java.util.Optional;
/**
* Constraints for certification request verification.
*
* <p>
* This policy controls proof-of-possession requirements and may carry
* framework-specific verification modes via optional hints.
* </p>
*
* @param requireProofOfPossession whether proof-of-possession is required
* @param compatibilityProfileId optional compatibility profile hint for
* parsers/verifiers
*/
public record VerificationPolicy(boolean requireProofOfPossession, Optional<String> compatibilityProfileId) {
/**
* Creates a verification policy.
*
* @param requireProofOfPossession PoP requirement
* @param compatibilityProfileId optional compatibility profile id
* @throws IllegalArgumentException if {@code compatibilityProfileId} is null
*/
public VerificationPolicy {
if (compatibilityProfileId == null) {
throw new IllegalArgumentException("compatibilityProfileId must not be null");
}
}
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Credential issuance domain model.
*
* <p>
* This package contains command objects and input types used to issue, renew,
* replace, and reissue credentials, as well as optional issuance verification
* policies. The operations are executed through
* {@link zeroecho.pki.api.IssuanceService}.
* </p>
*
* <h2>Command-driven operations</h2>
* <ul>
* <li>Issue end-entity credentials.</li>
* <li>Renew existing credentials.</li>
* <li>Replace credentials (e.g., due to key changes).</li>
* <li>Reissue credentials (policy-driven reissuance).</li>
* </ul>
*
* <p>
* Requests may originate from the request domain
* ({@code zeroecho.pki.api.request}) and issuance outcomes may be published
* and/or recorded in inventory.
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api.issuance;

View File

@@ -0,0 +1,114 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.orch;
/**
* Durability policy for workflow continuation data managed by orchestrators.
*
* <p>
* Orchestrators coordinate long-running operations (e.g., human approvals,
* remote signing, certificate issuance). Some workflows require local
* continuation state to resume after a JVM restart. This enum defines how such
* state is handled.
* </p>
*
* <h2>Examples</h2>
* <ul>
* <li>A remote signature workflow may wait for hours. If the remote system is
* the source of truth and all necessary data can be re-derived from external
* references (operation ID, request ID), the workflow can run in a durable mode
* without storing sensitive material locally.</li>
* <li>If a workflow requires local ephemeral secrets (e.g., a one-time approval
* challenge that cannot be recomputed), strict mode should abort on restart
* (fail-closed) unless encrypted persistence is configured.</li>
* </ul>
*/
public enum OrchestrationDurabilityPolicy {
/**
* Fail-closed behavior.
*
* <p>
* The orchestrator does not persist continuation state that would be required
* to resume the workflow after a process restart. If the process restarts,
* operations that cannot be safely resumed must be cancelled or marked as
* failed deterministically.
* </p>
*
* <p>
* Example: A workflow requires an ephemeral secret that is only available in
* memory. In this mode, the operation is aborted on restart and the client must
* resubmit.
* </p>
*/
STRICT_ABORT_ON_RESTART,
/**
* Durable operation using minimal non-sensitive state.
*
* <p>
* The orchestrator persists only references and non-sensitive continuation
* state, sufficient to resume the workflow after restart. Sensitive payloads
* must remain outside the local state (e.g., in dedicated stores or remote
* systems).
* </p>
*
* <p>
* Example: An operation persists a reference to a stored CSR and a reference to
* the selected certificate profile, but does not persist raw key material or
* private tokens.
* </p>
*/
DURABLE_MIN_STATE,
/**
* Durable operation with encrypted continuation state.
*
* <p>
* The orchestrator persists continuation state encrypted (e.g., AES-256/GCM)
* using a key supplied by external configuration (environment variable, secret
* store, or KMS). This allows workflows to resume after restart even when they
* require sensitive local state.
* </p>
*
* <p>
* Example: A workflow stores an encrypted blob containing a transient approval
* context. The encryption uses authenticated data derived from operation ID,
* operation type, and owner identity to prevent swapping blobs across
* operations.
* </p>
*/
DURABLE_ENCRYPTED_STATE
}

View File

@@ -0,0 +1,120 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.orch;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import zeroecho.pki.api.EncodedObject;
import zeroecho.pki.api.Encoding;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.audit.Principal;
/**
* Persisted continuation state for an orchestrated workflow.
*
* <p>
* This record is used by orchestrators to persist the minimum information
* needed to resume long-running workflows after a process restart, according to
* the selected {@link OrchestrationDurabilityPolicy}.
* </p>
*
* <h2>Storage model</h2>
* <p>
* The async bus is the authoritative source for operation lifecycle and status,
* but it does not persist sensitive payloads and may be configured not to
* persist results. Orchestrators therefore persist workflow-specific
* continuation state through {@code PkiStore}.
* </p>
*
* <h2>Security</h2>
* <ul>
* <li>{@link #payload()} may contain sensitive information. It must not be
* logged.</li>
* <li>When {@link #durabilityPolicy()} is
* {@link OrchestrationDurabilityPolicy#DURABLE_ENCRYPTED_STATE},
* {@link #payloadEncoding()} must reflect an encrypted form and the payload
* must be authenticated using AAD derived from operation identity.</li>
* </ul>
*
* @param opId async operation identifier (never {@code null})
* @param type operation type string (never {@code null})
* @param owner operation owner (never {@code null})
* @param durabilityPolicy durability policy (never {@code null})
* @param createdAt creation time (never {@code null})
* @param updatedAt last update time (never {@code null})
* @param expiresAt expiration/deadline (never {@code null})
* @param payloadEncoding encoding of {@link #payload()} (never {@code null})
* @param payload optional persisted continuation payload
*/
public record WorkflowStateRecord(PkiId opId, String type, Principal owner,
OrchestrationDurabilityPolicy durabilityPolicy, Instant createdAt, Instant updatedAt, Instant expiresAt,
Encoding payloadEncoding, Optional<EncodedObject> payload) {
public WorkflowStateRecord {
Objects.requireNonNull(opId, "opId");
Objects.requireNonNull(type, "type");
Objects.requireNonNull(owner, "owner");
Objects.requireNonNull(durabilityPolicy, "durabilityPolicy");
Objects.requireNonNull(createdAt, "createdAt");
Objects.requireNonNull(updatedAt, "updatedAt");
Objects.requireNonNull(expiresAt, "expiresAt");
Objects.requireNonNull(payloadEncoding, "payloadEncoding");
Objects.requireNonNull(payload, "payload");
if (type.isBlank()) {
throw new IllegalArgumentException("type must not be blank.");
}
if (!expiresAt.isAfter(createdAt) && !expiresAt.equals(createdAt)) {
// Allow expiresAt == createdAt for immediate-expire test scenarios, but reject
// negative durations.
if (expiresAt.isBefore(createdAt)) { // NOPMD
throw new IllegalArgumentException("expiresAt must not be before createdAt.");
}
}
}
/**
* Returns whether the record is expired at the provided time.
*
* @param now current time (never {@code null})
* @return {@code true} if {@code now} is strictly after {@link #expiresAt()}
*/
public boolean isExpiredAt(Instant now) {
Objects.requireNonNull(now, "now");
return now.isAfter(expiresAt);
}
}

View File

@@ -0,0 +1,114 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Orchestration and workflow durability API for ZeroEcho PKI.
*
* <p>
* This package defines stable domain abstractions related to <em>long-running
* PKI workflows</em> and their durability requirements. It is concerned with
* describing <strong>what must be persisted and how durable such persistence
* must be</strong>, not with <em>how</em> the persistence is implemented.
* </p>
*
* <h2>Scope and responsibilities</h2>
* <p>
* The orchestration layer in ZeroEcho PKI coordinates multi-step operations
* such as certificate issuance, revocation processing, key rollover, or
* publication workflows. These operations:
* </p>
* <ul>
* <li>may span multiple logical steps,</li>
* <li>may involve external systems or asynchronous callbacks,</li>
* <li>must tolerate restarts, crashes, or redeployments.</li>
* </ul>
*
* <p>
* This package provides the minimal API necessary to:
* </p>
* <ul>
* <li>describe the required durability guarantees for workflow state,</li>
* <li>represent persisted workflow state in a storage-agnostic form,</li>
* <li>enable deterministic recovery and audit of workflow progression.</li>
* </ul>
*
* <h2>Key abstractions</h2>
* <ul>
* <li>{@link zeroecho.pki.api.orch.OrchestrationDurabilityPolicy
* OrchestrationDurabilityPolicy} defines <em>how durable</em> orchestration
* state must be (e.g. write-through, buffered, best-effort), allowing different
* operational trade-offs without changing orchestration logic.</li>
* <li>{@link zeroecho.pki.api.orch.WorkflowStateRecord WorkflowStateRecord}
* represents an immutable, persisted snapshot of workflow state at a specific
* point in time, suitable for recovery, audit, and historical inspection.</li>
* </ul>
*
* <h2>Immutability and history</h2>
* <p>
* Workflow state records are designed to be immutable once persisted.
* Implementations are expected to append new state records rather than
* overwrite existing ones, enabling:
* </p>
* <ul>
* <li>full audit trails,</li>
* <li>temporal queries ("state at time T"),</li>
* <li>post-mortem analysis of failed or aborted workflows.</li>
* </ul>
*
* <p>
* Whether and for how long historical records are retained is governed by
* policy and implementation, but the API itself is intentionally compatible
* with history-preserving stores.
* </p>
*
* <h2>API stability and integration</h2>
* <p>
* The types in this package are part of the public PKI API surface. They are
* intended to be usable across different runtime environments, including:
* </p>
* <ul>
* <li>standalone CLI applications,</li>
* <li>long-running server processes,</li>
* <li>container-managed frameworks such as Spring or Micronaut.</li>
* </ul>
*
* <p>
* No assumptions are made about the underlying persistence mechanism
* (filesystem, database, distributed log, etc.). Such concerns are handled by
* SPI and implementation layers.
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api.orch;

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/**
* Public, framework-agnostic PKI API.
*
* <p>
* This package provides stable entry points and core value types for operating
* a PKI instance. The API is intentionally independent of any concrete
* certificate framework (for example X.509), allowing multiple frameworks to be
* integrated via SPIs in {@code zeroecho.pki.spi.*}.
* </p>
*
* <h2>Design principles</h2>
* <ul>
* <li><strong>Framework independence:</strong> the API models PKI concepts (CA,
* issuance, revocation, status objects, publication, backup/restore) without
* binding to a single certificate technology.</li>
* <li><strong>Explicit commands and queries:</strong> mutable operations are
* expressed as command objects and retrieval via query objects in
* subpackages.</li>
* <li><strong>Safety and auditability:</strong> security-relevant operations
* are designed to be auditable; sensitive data must never be exposed by API
* abstractions.</li>
* </ul>
*
* <h2>Key entry points</h2>
* <ul>
* <li>{@link zeroecho.pki.api.CaService}</li>
* <li>{@link zeroecho.pki.api.CertificationRequestService}</li>
* <li>{@link zeroecho.pki.api.IssuanceService}</li>
* <li>{@link zeroecho.pki.api.RevocationService}</li>
* <li>{@link zeroecho.pki.api.StatusObjectService}</li>
* <li>{@link zeroecho.pki.api.PublicationService}</li>
* <li>{@link zeroecho.pki.api.ProfileService}</li>
* <li>{@link zeroecho.pki.api.PolicyService}</li>
* <li>{@link zeroecho.pki.api.ImportExportService}</li>
* <li>{@link zeroecho.pki.api.BackupService}</li>
* </ul>
*
* <p>
* Subpackages further organize domain models: {@code ca}, {@code issuance},
* {@code request}, {@code revocation}, {@code status}, {@code publication},
* {@code profile}, {@code policy}, {@code transfer}, plus attribute and audit
* domains in {@code attr} and {@code audit}.
* </p>
*
* @since 1.0
*/
package zeroecho.pki.api;

View File

@@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.policy;
import java.util.List;
import zeroecho.pki.api.PkiId;
import zeroecho.pki.api.attr.AttributeSet;
/**
* Policy decision including optional modifications to be applied to an
* operation.
*
* <p>
* The {@code appliedOverrides} attribute set is used to communicate
* policy-enforced adjustments (e.g., constrained validity, normalized
* attributes). It must not contain secrets.
* </p>
*
* @param decisionId stable decision identifier for correlation and
* explainability
* @param status decision outcome status
* @param messages non-sensitive operator-readable messages
* @param appliedOverrides policy-enforced overrides to be applied downstream
*/
public record PolicyDecision(PkiId decisionId, PolicyDecisionStatus status, List<String> messages,
AttributeSet appliedOverrides) {
/**
* Creates a policy decision.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public PolicyDecision {
if (decisionId == null) {
throw new IllegalArgumentException("decisionId must not be null");
}
if (status == null) {
throw new IllegalArgumentException("status must not be null");
}
if (messages == null) {
throw new IllegalArgumentException("messages must not be null");
}
if (appliedOverrides == null) {
throw new IllegalArgumentException("appliedOverrides must not be null");
}
}
}

View File

@@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.policy;
/**
* Outcome status of a policy evaluation.
*/
public enum PolicyDecisionStatus {
/**
* Operation is allowed under current policy.
*/
ALLOW,
/**
* Operation is denied under current policy.
*/
DENY,
/**
* Operation is allowed, but policy requires modifications (e.g., validity
* truncation).
*/
ALLOW_WITH_MODIFICATIONS
}

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.policy;
import java.util.List;
import zeroecho.pki.api.PkiId;
/**
* Explainability trace for a policy decision.
*
* @param decisionId decision id this trace explains
* @param steps ordered evaluation steps
*/
public record PolicyTrace(PkiId decisionId, List<PolicyTraceStep> steps) {
/**
* Creates a policy trace.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public PolicyTrace {
if (decisionId == null) {
throw new IllegalArgumentException("decisionId must not be null");
}
if (steps == null) {
throw new IllegalArgumentException("steps must not be null");
}
}
}

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (C) 2025, Leo Galambos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software must
* display the following acknowledgement:
* This product includes software developed by the Egothor project.
*
* 4. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package zeroecho.pki.api.policy;
import java.util.List;
/**
* Single evaluation step within a policy trace.
*
* @param ruleId stable rule identifier (implementation-defined)
* @param outcome human-readable outcome string (e.g., "ALLOW", "DENY",
* "MODIFY")
* @param notes non-sensitive explanatory notes
*/
public record PolicyTraceStep(String ruleId, String outcome, List<String> notes) {
/**
* Creates a policy trace step.
*
* @throws IllegalArgumentException if inputs are invalid
*/
public PolicyTraceStep {
if (ruleId == null || ruleId.isBlank()) {
throw new IllegalArgumentException("ruleId must not be null/blank");
}
if (outcome == null || outcome.isBlank()) {
throw new IllegalArgumentException("outcome must not be null/blank");
}
if (notes == null) {
throw new IllegalArgumentException("notes must not be null");
}
}
}

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