chore(text): source code format
This commit is contained in:
@@ -138,18 +138,16 @@ public final class Guard {
|
||||
/**
|
||||
* Executes Guard with an explicit keyring unlock source.
|
||||
*
|
||||
* @param args command arguments
|
||||
* @param options dispatcher options
|
||||
* @param args command arguments
|
||||
* @param options dispatcher options
|
||||
* @param keyringUnlockProvider destroyable-password provider
|
||||
* @return process exit code
|
||||
* @throws ParseException if parsing fails
|
||||
* @throws IOException if I/O fails
|
||||
* @throws ParseException if parsing fails
|
||||
* @throws IOException if I/O fails
|
||||
* @throws GeneralSecurityException if cryptographic processing fails
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.NcssCount", "PMD.CognitiveComplexity",
|
||||
"PMD.CyclomaticComplexity", "PMD.NPathComplexity" })
|
||||
public static int main(final String[] args, final Options options,
|
||||
KeyringUnlockProvider keyringUnlockProvider)
|
||||
@SuppressWarnings({ "PMD.NcssCount", "PMD.CognitiveComplexity", "PMD.CyclomaticComplexity", "PMD.NPathComplexity" })
|
||||
public static int main(final String[] args, final Options options, KeyringUnlockProvider keyringUnlockProvider)
|
||||
throws ParseException, IOException, GeneralSecurityException {
|
||||
// ---- operation selection
|
||||
final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("in-file")
|
||||
@@ -206,8 +204,7 @@ public final class Guard {
|
||||
.desc("Recipient KEK length: exactly 16 or 32 bytes (default 32)").get();
|
||||
final Option OPT_PBKDF2_MAX = Option.builder().longOpt("pbkdf2-max").hasArg().argName("iterations")
|
||||
.desc("Operational PBKDF2 ceiling; required for password operations").get();
|
||||
final Option OPT_PBKDF2_HARD_MAX = Option.builder().longOpt("pbkdf2-hard-max").hasArg()
|
||||
.argName("iterations")
|
||||
final Option OPT_PBKDF2_HARD_MAX = Option.builder().longOpt("pbkdf2-hard-max").hasArg().argName("iterations")
|
||||
.desc("Absolute decoded PBKDF2 safety ceiling; required for password operations").get();
|
||||
|
||||
// ---- decoys (all types)
|
||||
@@ -260,8 +257,7 @@ public final class Guard {
|
||||
final CommandLine cmd = parser.parse(options, args);
|
||||
final boolean passwordOperation = cmd.hasOption(OPT_TO_PSW) || cmd.hasOption(OPT_DECOY_PSW)
|
||||
|| cmd.hasOption(OPT_DECOY_PSW_RAND) || cmd.hasOption(OPT_PASSWORD);
|
||||
final ZeroEchoSession session = createSession(cmd, OPT_PBKDF2_MAX, OPT_PBKDF2_HARD_MAX,
|
||||
passwordOperation);
|
||||
final ZeroEchoSession session = createSession(cmd, OPT_PBKDF2_MAX, OPT_PBKDF2_HARD_MAX, passwordOperation);
|
||||
|
||||
final boolean encrypt = cmd.hasOption(OPT_ENCRYPT);
|
||||
final Path inPath = Paths.get(cmd.getOptionValue(encrypt ? OPT_ENCRYPT : OPT_DECRYPT));
|
||||
@@ -359,8 +355,7 @@ public final class Guard {
|
||||
|
||||
// envelope builder (new API)
|
||||
final MultiRecipientDataSourceBuilder env = MultiRecipientDataSourceBuilder.builder(session)
|
||||
.payloadKeyBytes(cekBytes)
|
||||
.headerLimits(maxRecipients, maxEntryLen);
|
||||
.payloadKeyBytes(cekBytes).headerLimits(maxRecipients, maxEntryLen);
|
||||
UnlockMaterial borrowedUnlockMaterial = null;
|
||||
try (env) {
|
||||
if (aes != null) {
|
||||
@@ -374,11 +369,10 @@ public final class Guard {
|
||||
if (encrypt) {
|
||||
final int iter = Integer.parseInt(cmd.getOptionValue(OPT_PSW_ITER, "200000"));
|
||||
final int saltLen = Integer.parseInt(cmd.getOptionValue(OPT_PSW_SALT, "16"));
|
||||
final int kekLen = RecipientKekSizes.requireSupported(
|
||||
Integer.parseInt(cmd.getOptionValue(OPT_PSW_KEK, "32")));
|
||||
final int kekLen = RecipientKekSizes
|
||||
.requireSupported(Integer.parseInt(cmd.getOptionValue(OPT_PSW_KEK, "32")));
|
||||
|
||||
KeyringStore ks = loadKeyringIfPresent(cmd, OPT_KEYRING,
|
||||
keyringUnlockProvider);
|
||||
KeyringStore ks = loadKeyringIfPresent(cmd, OPT_KEYRING, keyringUnlockProvider);
|
||||
try (ks) {
|
||||
for (String alias : cmd.getOptionValues(OPT_TO_ALIAS) == null ? new String[0]
|
||||
: cmd.getOptionValues(OPT_TO_ALIAS)) {
|
||||
@@ -424,8 +418,7 @@ public final class Guard {
|
||||
throw new ParseException("Specify exactly one of --priv-alias or --password for decryption");
|
||||
}
|
||||
if (privAlias != null) {
|
||||
try (KeyringStore ks = requireKeyring(cmd, OPT_KEYRING,
|
||||
keyringUnlockProvider)) {
|
||||
try (KeyringStore ks = requireKeyring(cmd, OPT_KEYRING, keyringUnlockProvider)) {
|
||||
final KeyringStore.PrivateWithId pr = ks.getPrivateWithId(privAlias);
|
||||
borrowedUnlockMaterial = new UnlockMaterial.Private(pr.key());
|
||||
}
|
||||
@@ -474,8 +467,7 @@ public final class Guard {
|
||||
boolean hasAbsoluteMaximum = cmd.hasOption(absoluteMaximumOption);
|
||||
if (!hasOperationalMaximum && !hasAbsoluteMaximum) {
|
||||
if (passwordOperation) {
|
||||
throw new ParseException(
|
||||
"Password operations require --pbkdf2-max and --pbkdf2-hard-max");
|
||||
throw new ParseException("Password operations require --pbkdf2-max and --pbkdf2-hard-max");
|
||||
}
|
||||
return new ZeroEchoSession();
|
||||
}
|
||||
@@ -485,11 +477,9 @@ public final class Guard {
|
||||
try {
|
||||
int operationalMaximum = Integer.parseInt(cmd.getOptionValue(operationalMaximumOption));
|
||||
int absoluteMaximum = Integer.parseInt(cmd.getOptionValue(absoluteMaximumOption));
|
||||
return new ZeroEchoSession().withPbkdf2Limits(
|
||||
new Pbkdf2Limits(operationalMaximum, absoluteMaximum));
|
||||
return new ZeroEchoSession().withPbkdf2Limits(new Pbkdf2Limits(operationalMaximum, absoluteMaximum));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
ParseException parseException =
|
||||
new ParseException("Invalid PBKDF2 limits: " + exception.getMessage());
|
||||
ParseException parseException = new ParseException("Invalid PBKDF2 limits: " + exception.getMessage());
|
||||
parseException.initCause(exception);
|
||||
throw parseException;
|
||||
}
|
||||
@@ -524,9 +514,9 @@ public final class Guard {
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* In both cases, the created context is consumed by
|
||||
* the matching {@link MultiRecipientDataSourceBuilder} recipient method and is
|
||||
* closed internally by the resulting content.
|
||||
* In both cases, the created context is consumed by the matching
|
||||
* {@link MultiRecipientDataSourceBuilder} recipient method and is closed
|
||||
* internally by the resulting content.
|
||||
* </p>
|
||||
*
|
||||
* @param env target builder to which the recipient is added
|
||||
@@ -542,8 +532,8 @@ public final class Guard {
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.CloseResource", "PMD.UseTryWithResources" })
|
||||
private static void addRecipientFromAlias(ZeroEchoSession session, MultiRecipientDataSourceBuilder env,
|
||||
KeyringStore ks, String alias,
|
||||
int kekBytes, int saltLen, boolean decoy) throws GeneralSecurityException, IOException {
|
||||
KeyringStore ks, String alias, int kekBytes, int saltLen, boolean decoy)
|
||||
throws GeneralSecurityException, IOException {
|
||||
KeyringStore.PublicWithId r = ks.getPublicWithId(alias);
|
||||
final String algId = r.algorithm();
|
||||
final java.security.PublicKey pub = r.key();
|
||||
@@ -602,16 +592,14 @@ public final class Guard {
|
||||
}
|
||||
|
||||
private static KeyringStore loadKeyringIfPresent(CommandLine cmd, Option optKs,
|
||||
KeyringUnlockProvider unlockProvider)
|
||||
throws IOException, GeneralSecurityException {
|
||||
KeyringUnlockProvider unlockProvider) throws IOException, GeneralSecurityException {
|
||||
if (!cmd.hasOption(optKs)) {
|
||||
return null;
|
||||
}
|
||||
return KeyringUnlocks.open(Paths.get(cmd.getOptionValue(optKs)), unlockProvider);
|
||||
}
|
||||
|
||||
private static KeyringStore requireKeyring(CommandLine cmd, Option optKs,
|
||||
KeyringUnlockProvider unlockProvider)
|
||||
private static KeyringStore requireKeyring(CommandLine cmd, Option optKs, KeyringUnlockProvider unlockProvider)
|
||||
throws IOException, ParseException, GeneralSecurityException {
|
||||
if (!cmd.hasOption(optKs)) {
|
||||
throw new ParseException("--keyring <file> is required when aliases are used");
|
||||
|
||||
@@ -210,30 +210,27 @@ public final class Kem { // NOPMD
|
||||
* @param args command arguments
|
||||
* @param opts command options
|
||||
* @return process exit code
|
||||
* @throws ParseException if arguments are invalid
|
||||
* @throws IOException if I/O fails
|
||||
* @throws ParseException if arguments are invalid
|
||||
* @throws IOException if I/O fails
|
||||
* @throws GeneralSecurityException if cryptographic processing fails
|
||||
*/
|
||||
public static int main(String[] args, Options opts)
|
||||
throws ParseException, IOException, GeneralSecurityException {
|
||||
public static int main(String[] args, Options opts) throws ParseException, IOException, GeneralSecurityException {
|
||||
return main(args, opts, KeyringUnlocks.console());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the KEM command with an explicit keyring unlock source.
|
||||
*
|
||||
* @param args command arguments
|
||||
* @param opts command options
|
||||
* @param args command arguments
|
||||
* @param opts command options
|
||||
* @param unlockProvider keyring password provider
|
||||
* @return process exit code
|
||||
* @throws ParseException if arguments are invalid
|
||||
* @throws IOException if I/O fails
|
||||
* @throws ParseException if arguments are invalid
|
||||
* @throws IOException if I/O fails
|
||||
* @throws GeneralSecurityException if cryptographic processing fails
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.NcssCount", "PMD.CyclomaticComplexity",
|
||||
"PMD.NPathComplexity" })
|
||||
public static int main(String[] args, Options opts,
|
||||
KeyringUnlockProvider unlockProvider)
|
||||
@SuppressWarnings({ "PMD.NcssCount", "PMD.CyclomaticComplexity", "PMD.NPathComplexity" })
|
||||
public static int main(String[] args, Options opts, KeyringUnlockProvider unlockProvider)
|
||||
throws ParseException, IOException, GeneralSecurityException {
|
||||
ZeroEchoSession session = new ZeroEchoSession();
|
||||
defineOptions(opts);
|
||||
@@ -275,105 +272,105 @@ public final class Kem { // NOPMD
|
||||
final Path keyringPath = Path.of(cmd.getOptionValue(OPT_KEYRING.getLongOpt()));
|
||||
try (KeyringStore keyring = KeyringUnlocks.open(keyringPath, unlockProvider)) {
|
||||
|
||||
// Configure KEM envelope
|
||||
KemDataContentBuilder kem = KemDataContentBuilder.builder(session).kem(kemId);
|
||||
if (cmd.hasOption(OPT_DIRECT.getLongOpt())) {
|
||||
kem = kem.directSecret();
|
||||
} else {
|
||||
byte[] info = parseOptionalHex(cmd, OPT_HKDF, "ZeroEcho-KEM".getBytes());
|
||||
kem = kem.hkdfSha256(info);
|
||||
}
|
||||
// typed numeric options
|
||||
Integer keyBytes = parsedIntOpt(cmd, OPT_KEY_BYTES);
|
||||
if (keyBytes != null) {
|
||||
kem = kem.derivedKeyBytes(keyBytes);
|
||||
}
|
||||
Integer maxKemCt = parsedIntOpt(cmd, OPT_MAX_KEM_CT);
|
||||
if (maxKemCt != null) {
|
||||
kem = kem.maxKemCiphertextLen(maxKemCt);
|
||||
}
|
||||
// Configure KEM envelope
|
||||
KemDataContentBuilder kem = KemDataContentBuilder.builder(session).kem(kemId);
|
||||
if (cmd.hasOption(OPT_DIRECT.getLongOpt())) {
|
||||
kem = kem.directSecret();
|
||||
} else {
|
||||
byte[] info = parseOptionalHex(cmd, OPT_HKDF, "ZeroEcho-KEM".getBytes());
|
||||
kem = kem.hkdfSha256(info);
|
||||
}
|
||||
// typed numeric options
|
||||
Integer keyBytes = parsedIntOpt(cmd, OPT_KEY_BYTES);
|
||||
if (keyBytes != null) {
|
||||
kem = kem.derivedKeyBytes(keyBytes);
|
||||
}
|
||||
Integer maxKemCt = parsedIntOpt(cmd, OPT_MAX_KEM_CT);
|
||||
if (maxKemCt != null) {
|
||||
kem = kem.maxKemCiphertextLen(maxKemCt);
|
||||
}
|
||||
|
||||
// Common symmetric knobs
|
||||
final byte[] aad = parseHexOpt(cmd, OPT_AAD);
|
||||
final boolean wantHeader = cmd.hasOption(OPT_HEADER.getLongOpt());
|
||||
// Common symmetric knobs
|
||||
final byte[] aad = parseHexOpt(cmd, OPT_AAD);
|
||||
final boolean wantHeader = cmd.hasOption(OPT_HEADER.getLongOpt());
|
||||
|
||||
// AES payload
|
||||
if (wantAes) {
|
||||
String mode = cmd.getOptionValue(OPT_AES_CIPHER.getLongOpt(), "gcm").toLowerCase(Locale.ROOT);
|
||||
AesDataContentBuilder aes = AesDataContentBuilder.builder(session);
|
||||
switch (mode) {
|
||||
case "gcm" -> {
|
||||
Integer tagBitsOpt = parsedIntOpt(cmd, OPT_AES_TAG_BITS);
|
||||
int tagBits = tagBitsOpt == null ? 128 : tagBitsOpt;
|
||||
// AES payload
|
||||
if (wantAes) {
|
||||
String mode = cmd.getOptionValue(OPT_AES_CIPHER.getLongOpt(), "gcm").toLowerCase(Locale.ROOT);
|
||||
AesDataContentBuilder aes = AesDataContentBuilder.builder(session);
|
||||
switch (mode) {
|
||||
case "gcm" -> {
|
||||
Integer tagBitsOpt = parsedIntOpt(cmd, OPT_AES_TAG_BITS);
|
||||
int tagBits = tagBitsOpt == null ? 128 : tagBitsOpt;
|
||||
|
||||
aes = aes.modeGcm(tagBits);
|
||||
aes = aes.modeGcm(tagBits);
|
||||
}
|
||||
case "ctr" -> aes = aes.modeCtr();
|
||||
case "cbc" -> aes = aes.modeCbcPkcs5();
|
||||
default -> throw new ParseException("Unsupported --aes-cipher: " + mode);
|
||||
}
|
||||
case "ctr" -> aes = aes.modeCtr();
|
||||
case "cbc" -> aes = aes.modeCbcPkcs5();
|
||||
default -> throw new ParseException("Unsupported --aes-cipher: " + mode);
|
||||
byte[] iv = parseHexOpt(cmd, OPT_AES_IV);
|
||||
if (iv != null) {
|
||||
aes = aes.withDecryptionIv(iv);
|
||||
}
|
||||
if (aad != null && aad.length > 0) {
|
||||
aes = aes.withAad(aad);
|
||||
}
|
||||
if (wantHeader) {
|
||||
aes = aes.withHeader();
|
||||
}
|
||||
kem = kem.withAes(aes);
|
||||
}
|
||||
byte[] iv = parseHexOpt(cmd, OPT_AES_IV);
|
||||
if (iv != null) {
|
||||
aes = aes.withDecryptionIv(iv);
|
||||
}
|
||||
if (aad != null && aad.length > 0) {
|
||||
aes = aes.withAad(aad);
|
||||
}
|
||||
if (wantHeader) {
|
||||
aes = aes.withHeader();
|
||||
}
|
||||
kem = kem.withAes(aes);
|
||||
}
|
||||
|
||||
// ChaCha payload
|
||||
if (wantChaCha) {
|
||||
ChaChaDataContentBuilder cc = ChaChaDataContentBuilder.builder(session);
|
||||
byte[] nonce = parseHexOpt(cmd, OPT_CHACHA_NONCE);
|
||||
if (nonce != null) {
|
||||
cc = cc.withDecryptionNonce(nonce);
|
||||
// ChaCha payload
|
||||
if (wantChaCha) {
|
||||
ChaChaDataContentBuilder cc = ChaChaDataContentBuilder.builder(session);
|
||||
byte[] nonce = parseHexOpt(cmd, OPT_CHACHA_NONCE);
|
||||
if (nonce != null) {
|
||||
cc = cc.withDecryptionNonce(nonce);
|
||||
}
|
||||
// counter is an integer, not bytes; use typed parsed option
|
||||
Integer counter = parsedIntOpt(cmd, OPT_CHACHA_COUNTER);
|
||||
if (counter != null) {
|
||||
cc = cc.withCounter(counter);
|
||||
}
|
||||
Integer initial = parsedIntOpt(cmd, OPT_CHACHA_INITIAL);
|
||||
if (initial != null) {
|
||||
cc = cc.initialCounter(initial);
|
||||
}
|
||||
if (aad != null && aad.length > 0) {
|
||||
cc = cc.withAad(aad); // selects AEAD
|
||||
}
|
||||
if (wantHeader) {
|
||||
cc = cc.withHeader();
|
||||
}
|
||||
kem = kem.withChaCha(cc);
|
||||
}
|
||||
// counter is an integer, not bytes; use typed parsed option
|
||||
Integer counter = parsedIntOpt(cmd, OPT_CHACHA_COUNTER);
|
||||
if (counter != null) {
|
||||
cc = cc.withCounter(counter);
|
||||
}
|
||||
Integer initial = parsedIntOpt(cmd, OPT_CHACHA_INITIAL);
|
||||
if (initial != null) {
|
||||
cc = cc.initialCounter(initial);
|
||||
}
|
||||
if (aad != null && aad.length > 0) {
|
||||
cc = cc.withAad(aad); // selects AEAD
|
||||
}
|
||||
if (wantHeader) {
|
||||
cc = cc.withHeader();
|
||||
}
|
||||
kem = kem.withChaCha(cc);
|
||||
}
|
||||
|
||||
// Pipeline: source -> kem payload stage
|
||||
DataContent chain;
|
||||
if (encrypt) {
|
||||
String alias = require(cmd, OPT_PUB, "Missing --pub for encryption");
|
||||
PublicKey recipient = keyring.getPublic(alias);
|
||||
chain = DataContentChainBuilder.encrypt()
|
||||
.add(PlainFileBuilder.builder().url(Path.of(input).toUri().toURL()))
|
||||
.add(kem.recipientPublic(recipient)).build();
|
||||
} else {
|
||||
String alias = require(cmd, OPT_PRIV, "Missing --priv for decryption");
|
||||
PrivateKey recipient = keyring.getPrivate(alias);
|
||||
chain = DataContentChainBuilder.decrypt()
|
||||
.add(PlainFileBuilder.builder().url(Path.of(input).toUri().toURL()))
|
||||
.add(kem.recipientPrivate(recipient)).build();
|
||||
}
|
||||
|
||||
try (InputStream in = chain.getStream(); OutputStream out = Files.newOutputStream(output)) {
|
||||
in.transferTo(out);
|
||||
} catch (IOException ex) {
|
||||
if (LOG.isLoggable(Level.SEVERE)) {
|
||||
LOG.log(Level.SEVERE, "I/O error", ex);
|
||||
// Pipeline: source -> kem payload stage
|
||||
DataContent chain;
|
||||
if (encrypt) {
|
||||
String alias = require(cmd, OPT_PUB, "Missing --pub for encryption");
|
||||
PublicKey recipient = keyring.getPublic(alias);
|
||||
chain = DataContentChainBuilder.encrypt()
|
||||
.add(PlainFileBuilder.builder().url(Path.of(input).toUri().toURL()))
|
||||
.add(kem.recipientPublic(recipient)).build();
|
||||
} else {
|
||||
String alias = require(cmd, OPT_PRIV, "Missing --priv for decryption");
|
||||
PrivateKey recipient = keyring.getPrivate(alias);
|
||||
chain = DataContentChainBuilder.decrypt()
|
||||
.add(PlainFileBuilder.builder().url(Path.of(input).toUri().toURL()))
|
||||
.add(kem.recipientPrivate(recipient)).build();
|
||||
}
|
||||
|
||||
try (InputStream in = chain.getStream(); OutputStream out = Files.newOutputStream(output)) {
|
||||
in.transferTo(out);
|
||||
} catch (IOException ex) {
|
||||
if (LOG.isLoggable(Level.SEVERE)) {
|
||||
LOG.log(Level.SEVERE, "I/O error", ex);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,16 +192,15 @@ public final class KeyStoreManagement {
|
||||
/**
|
||||
* Executes the command with an explicit unlock source.
|
||||
*
|
||||
* @param args arguments passed by the application dispatcher
|
||||
* @param args arguments passed by the application dispatcher
|
||||
* @param dispatcherOptions dispatcher options
|
||||
* @param unlockProvider explicit destroyable-password provider
|
||||
* @param unlockProvider explicit destroyable-password provider
|
||||
* @return process exit code
|
||||
* @throws ParseException if parsing fails
|
||||
* @throws IOException if keyring I/O fails
|
||||
* @throws ParseException if parsing fails
|
||||
* @throws IOException if keyring I/O fails
|
||||
* @throws GeneralSecurityException if cryptographic processing fails
|
||||
*/
|
||||
public static int main(final String[] args, final Options dispatcherOptions,
|
||||
KeyringUnlockProvider unlockProvider)
|
||||
public static int main(final String[] args, final Options dispatcherOptions, KeyringUnlockProvider unlockProvider)
|
||||
throws ParseException, IOException, GeneralSecurityException {
|
||||
ZeroEchoSession session = new ZeroEchoSession();
|
||||
defineOptions(dispatcherOptions);
|
||||
@@ -218,8 +217,7 @@ public final class KeyStoreManagement {
|
||||
}
|
||||
|
||||
Path keyringPath = Path.of(cmd.getOptionValue(KEYSTORE_OPTION.getLongOpt()));
|
||||
try (KeyringStore store = Files.exists(keyringPath)
|
||||
? KeyringUnlocks.open(keyringPath, unlockProvider)
|
||||
try (KeyringStore store = Files.exists(keyringPath) ? KeyringUnlocks.open(keyringPath, unlockProvider)
|
||||
: KeyringUnlocks.create(keyringPath, unlockProvider)) {
|
||||
if (cmd.hasOption(LIST_ALIASES_OPTION.getLongOpt())) {
|
||||
listAliases(store);
|
||||
@@ -310,8 +308,8 @@ public final class KeyStoreManagement {
|
||||
* @param store keyring store to mutate
|
||||
* @param cmd parsed command line
|
||||
*/
|
||||
public static void doGenerate(final ZeroEchoSession session, final KeyringStore store,
|
||||
final CommandLine cmd) throws IOException, GeneralSecurityException {
|
||||
public static void doGenerate(final ZeroEchoSession session, final KeyringStore store, final CommandLine cmd)
|
||||
throws IOException, GeneralSecurityException {
|
||||
String algId = required(cmd, ALG_OPTION, "--alg is required for --generate");
|
||||
String aliasBase = required(cmd, ALIAS_OPTION, "--alias is required for --generate");
|
||||
String kind = cmd.getOptionValue(KIND_OPTION.getLongOpt());
|
||||
@@ -391,8 +389,7 @@ public final class KeyStoreManagement {
|
||||
}
|
||||
|
||||
private static void generateSymmetric(ZeroEchoSession session, KeyringStore store, CryptoAlgorithm algorithm,
|
||||
String algorithmId, String alias, boolean overwrite)
|
||||
throws IOException, GeneralSecurityException {
|
||||
String algorithmId, String alias, boolean overwrite) throws IOException, GeneralSecurityException {
|
||||
GeneratedSecret generated = firstGeneratedSecret(session, algorithm, algorithmId);
|
||||
ensureWritable(store, alias, overwrite);
|
||||
store.putSecret(alias, algorithmId, generated.key());
|
||||
@@ -430,9 +427,7 @@ public final class KeyStoreManagement {
|
||||
|
||||
/** Selects the exact key-generation operation requested by the command. */
|
||||
private enum GenerationKind {
|
||||
ASYMMETRIC,
|
||||
SYMMETRIC,
|
||||
NONE
|
||||
ASYMMETRIC, SYMMETRIC, NONE
|
||||
}
|
||||
|
||||
private static String required(CommandLine cmd, Option opt, String message) {
|
||||
|
||||
@@ -53,8 +53,7 @@ final class KeyringUnlocks {
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyringPassword acquire(KeyringUnlockProvider provider)
|
||||
throws IOException {
|
||||
private static KeyringPassword acquire(KeyringUnlockProvider provider) throws IOException {
|
||||
KeyringPassword password = provider.acquire();
|
||||
if (password == null) {
|
||||
throw new IOException("Keyring unlock provider returned no password");
|
||||
|
||||
@@ -158,24 +158,22 @@ public final class Tag { // NOPMD
|
||||
* @throws GeneralSecurityException if a cryptographic error occurs during
|
||||
* signature or digest processing
|
||||
*/
|
||||
public static int main(String[] args, Options root)
|
||||
throws ParseException, IOException, GeneralSecurityException {
|
||||
public static int main(String[] args, Options root) throws ParseException, IOException, GeneralSecurityException {
|
||||
return main(args, root, KeyringUnlocks.console());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command with an explicit keyring unlock source.
|
||||
*
|
||||
* @param args command arguments
|
||||
* @param root root options
|
||||
* @param args command arguments
|
||||
* @param root root options
|
||||
* @param unlockProvider keyring password provider
|
||||
* @return process exit code
|
||||
* @throws ParseException if parsing fails
|
||||
* @throws IOException if I/O fails
|
||||
* @throws ParseException if parsing fails
|
||||
* @throws IOException if I/O fails
|
||||
* @throws GeneralSecurityException if cryptographic processing fails
|
||||
*/
|
||||
public static int main(String[] args, Options root,
|
||||
KeyringUnlockProvider unlockProvider)
|
||||
public static int main(String[] args, Options root, KeyringUnlockProvider unlockProvider)
|
||||
throws ParseException, IOException, GeneralSecurityException {
|
||||
ZeroEchoSession session = new ZeroEchoSession();
|
||||
Options opts = root;
|
||||
@@ -222,13 +220,13 @@ public final class Tag { // NOPMD
|
||||
if (produce) {
|
||||
String privAlias = require(cli, PRIV_OPT, "signature produce requires --priv <alias>");
|
||||
PrivateKey priv = keyring.getPrivate(privAlias);
|
||||
tail = new TagTrailerDataContentBuilder<>(
|
||||
TagEngineBuilder.signature(session, alg, priv, spec)).build(true);
|
||||
tail = new TagTrailerDataContentBuilder<>(TagEngineBuilder.signature(session, alg, priv, spec))
|
||||
.build(true);
|
||||
} else {
|
||||
String pubAlias = require(cli, PUB_OPT, "signature verify requires --pub <alias>");
|
||||
PublicKey pub = keyring.getPublic(pubAlias);
|
||||
tail = new TagTrailerDataContentBuilder<>(
|
||||
TagEngineBuilder.signature(session, alg, pub, spec)).build(false);
|
||||
tail = new TagTrailerDataContentBuilder<>(TagEngineBuilder.signature(session, alg, pub, spec))
|
||||
.build(false);
|
||||
}
|
||||
}
|
||||
} else { // digest
|
||||
|
||||
@@ -124,16 +124,16 @@ public class GuardTest {
|
||||
|
||||
// Encrypt
|
||||
String[] encArgs = { "--encrypt", in.toString(), "--output", enc.toString(), "--to-psw", password,
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg",
|
||||
"aes-gcm", "--tag-bits", Integer.toString(tagBits), "--aad-hex", aadHex };
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg", "aes-gcm",
|
||||
"--tag-bits", Integer.toString(tagBits), "--aad-hex", aadHex };
|
||||
System.out.println("...encrypt: " + Arrays.toString(encArgs));
|
||||
int e = Guard.main(encArgs, new Options(), TestKeyringUnlocks.provider());
|
||||
assertEquals(0, e, "... encrypt expected exit code 0");
|
||||
|
||||
// Decrypt (using password)
|
||||
String[] decArgs = { "--decrypt", enc.toString(), "--output", dec.toString(), "--password", password,
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg",
|
||||
"aes-gcm", "--tag-bits", Integer.toString(tagBits), "--aad-hex", aadHex };
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg", "aes-gcm",
|
||||
"--tag-bits", Integer.toString(tagBits), "--aad-hex", aadHex };
|
||||
System.out.println("...decrypt: " + Arrays.toString(decArgs));
|
||||
int d = Guard.main(decArgs, new Options(), TestKeyringUnlocks.provider());
|
||||
assertEquals(0, d, "... decrypt expected exit code 0");
|
||||
@@ -163,10 +163,8 @@ public class GuardTest {
|
||||
System.out.println(method);
|
||||
Path input = writeRandom(tmp.resolve("invalid-kek.bin"), 32, 0x4B454B);
|
||||
Path output = tmp.resolve("invalid-kek.enc");
|
||||
String[] arguments = { "--encrypt", input.toString(), "--output", output.toString(),
|
||||
"--to-psw", "controlled", "--to-kek-bytes", "24",
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM,
|
||||
"--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM,
|
||||
String[] arguments = { "--encrypt", input.toString(), "--output", output.toString(), "--to-psw", "controlled",
|
||||
"--to-kek-bytes", "24", "--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM,
|
||||
"--alg", "aes-gcm" };
|
||||
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
@@ -290,8 +288,8 @@ public class GuardTest {
|
||||
// plus 2 random password decoys. Recipients are shuffled by default.
|
||||
String[] encArgs = { "--encrypt", in.toString(), "--output", enc.toString(), "--keyring", ring.toString(),
|
||||
"--to-alias", rsa.pub, "--to-psw", password, "--decoy-alias", elg.pub, "--decoy-psw-rand", "2",
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg",
|
||||
"aes-gcm", "--tag-bits", Integer.toString(tagBits), "--aad-hex", aad };
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg", "aes-gcm",
|
||||
"--tag-bits", Integer.toString(tagBits), "--aad-hex", aad };
|
||||
System.out.println("...encrypt: " + Arrays.toString(encArgs));
|
||||
int e = Guard.main(encArgs, new Options(), TestKeyringUnlocks.provider());
|
||||
assertEquals(0, e, "... encrypt rc");
|
||||
@@ -308,8 +306,8 @@ public class GuardTest {
|
||||
|
||||
// Decrypt via password instead of key
|
||||
String[] decPwd = { "--decrypt", enc.toString(), "--output", dec2.toString(), "--password", password,
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg",
|
||||
"aes-gcm", "--tag-bits", Integer.toString(tagBits), "--aad-hex", aad };
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg", "aes-gcm",
|
||||
"--tag-bits", Integer.toString(tagBits), "--aad-hex", aad };
|
||||
System.out.println("...decrypt(password): " + Arrays.toString(decPwd));
|
||||
int d2 = Guard.main(decPwd, new Options(), TestKeyringUnlocks.provider());
|
||||
assertEquals(0, d2, "... decrypt(password) rc");
|
||||
@@ -334,9 +332,9 @@ public class GuardTest {
|
||||
Path enc = tmp.resolve("pt-neg.bin.enc");
|
||||
String pwd = "x";
|
||||
|
||||
String[] encArgs = { "--encrypt", in.toString(), "--output", enc.toString(), "--to-psw", pwd,
|
||||
"--pbkdf2-max", TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg",
|
||||
"aes-gcm", "--tag-bits", "128" };
|
||||
String[] encArgs = { "--encrypt", in.toString(), "--output", enc.toString(), "--to-psw", pwd, "--pbkdf2-max",
|
||||
TEST_PBKDF2_MAXIMUM, "--pbkdf2-hard-max", TEST_PBKDF2_MAXIMUM, "--alg", "aes-gcm", "--tag-bits",
|
||||
"128" };
|
||||
int e = Guard.main(encArgs, new Options(), TestKeyringUnlocks.provider());
|
||||
assertEquals(0, e, "... encrypt rc");
|
||||
|
||||
|
||||
@@ -165,8 +165,7 @@ public class KemTest {
|
||||
KeyAliases aliases = generateKemIntoKeyStore(ring, kemId, "alias-" + shortId(kemId));
|
||||
|
||||
// Sanity: re-open to ensure the file is valid
|
||||
try (zeroecho.core.storage.KeyringPassword password =
|
||||
TestKeyringUnlocks.provider().acquire();
|
||||
try (zeroecho.core.storage.KeyringPassword password = TestKeyringUnlocks.provider().acquire();
|
||||
KeyringStore ks = KeyringStore.open(ring, password)) {
|
||||
if (!(ks.contains(aliases.pub) && ks.contains(aliases.prv))) {
|
||||
throw new IllegalStateException("Keyring does not contain expected aliases for " + kemId);
|
||||
@@ -210,15 +209,15 @@ public class KemTest {
|
||||
Files.write(plain, content);
|
||||
System.out.println("...[" + kemId + "] ChaCha encrypt");
|
||||
int e = Kem.main(new String[] { "--encrypt", plain.toString(), "--output", enc.toString(),
|
||||
"--keyring", ring.toString(), "--pub", aliases.pub, "--kem", kemId, "--chacha",
|
||||
"--aad", aadChaCha, "--header" }, new Options(), TestKeyringUnlocks.provider());
|
||||
"--keyring", ring.toString(), "--pub", aliases.pub, "--kem", kemId, "--chacha", "--aad",
|
||||
aadChaCha, "--header" }, new Options(), TestKeyringUnlocks.provider());
|
||||
if (e != 0) {
|
||||
throw new IllegalStateException("ChaCha encrypt rc=" + e);
|
||||
}
|
||||
System.out.println("...[" + kemId + "] ChaCha decrypt");
|
||||
int d = Kem.main(new String[] { "--decrypt", enc.toString(), "--output", dec.toString(),
|
||||
"--keyring", ring.toString(), "--priv", aliases.prv, "--kem", kemId, "--chacha",
|
||||
"--aad", aadChaCha, "--header" }, new Options(), TestKeyringUnlocks.provider());
|
||||
"--keyring", ring.toString(), "--priv", aliases.prv, "--kem", kemId, "--chacha", "--aad",
|
||||
aadChaCha, "--header" }, new Options(), TestKeyringUnlocks.provider());
|
||||
if (d != 0) {
|
||||
throw new IllegalStateException("ChaCha decrypt rc=" + d);
|
||||
}
|
||||
|
||||
@@ -139,8 +139,7 @@ public class KeyStoreManagementTest {
|
||||
assertTrue(attempted > 0, "No generation attempts were successful");
|
||||
|
||||
// Verify by reloading and materializing.
|
||||
zeroecho.core.storage.KeyringPassword password =
|
||||
TestKeyringUnlocks.provider().acquire();
|
||||
zeroecho.core.storage.KeyringPassword password = TestKeyringUnlocks.provider().acquire();
|
||||
KeyringStore store;
|
||||
try {
|
||||
store = KeyringStore.open(ring, password);
|
||||
@@ -197,15 +196,13 @@ public class KeyStoreManagementTest {
|
||||
// ---- helpers ----
|
||||
|
||||
private static boolean hasAsymmetricDefault(CryptoAlgorithm alg) {
|
||||
return alg.keyOperations().stream()
|
||||
.anyMatch(info -> info.operation() == KeyOperation.ASYMMETRIC_KEY_PAIR_GENERATE
|
||||
&& info.defaultSpec() != null);
|
||||
return alg.keyOperations().stream().anyMatch(
|
||||
info -> info.operation() == KeyOperation.ASYMMETRIC_KEY_PAIR_GENERATE && info.defaultSpec() != null);
|
||||
}
|
||||
|
||||
private static boolean hasSymmetricDefault(CryptoAlgorithm alg) {
|
||||
return alg.keyOperations().stream()
|
||||
.anyMatch(info -> info.operation() == KeyOperation.SYMMETRIC_GENERATE
|
||||
&& info.defaultSpec() != null);
|
||||
.anyMatch(info -> info.operation() == KeyOperation.SYMMETRIC_GENERATE && info.defaultSpec() != null);
|
||||
}
|
||||
|
||||
private static String sanitize(String id) {
|
||||
|
||||
@@ -127,8 +127,7 @@ public class TagTest {
|
||||
Path ring = tmp.resolve("ring-ed25519.txt");
|
||||
KeyAliases ed = generateIntoKeyStore(ring, "Ed25519", "ed");
|
||||
// sanity
|
||||
try (zeroecho.core.storage.KeyringPassword password =
|
||||
TestKeyringUnlocks.provider().acquire();
|
||||
try (zeroecho.core.storage.KeyringPassword password = TestKeyringUnlocks.provider().acquire();
|
||||
KeyringStore ks = KeyringStore.open(ring, password)) {
|
||||
assertTrue(ks.contains(ed.pub) && ks.contains(ed.prv), "missing expected aliases");
|
||||
}
|
||||
@@ -200,12 +199,18 @@ public class TagTest {
|
||||
Files.write(plain, pt);
|
||||
|
||||
// produce
|
||||
assertEquals(0, Tag.main(new String[] { "--type", "digest", "--mode", "produce", "--alg", "SHA-256", "--in",
|
||||
plain.toString(), "--out", tagged.toString() }, new Options(), TestKeyringUnlocks.provider()));
|
||||
assertEquals(0,
|
||||
Tag.main(
|
||||
new String[] { "--type", "digest", "--mode", "produce", "--alg", "SHA-256", "--in",
|
||||
plain.toString(), "--out", tagged.toString() },
|
||||
new Options(), TestKeyringUnlocks.provider()));
|
||||
|
||||
// verify (match)
|
||||
assertEquals(0, Tag.main(new String[] { "--type", "digest", "--mode", "verify", "--alg", "SHA-256", "--in",
|
||||
tagged.toString(), "--out", recovered.toString() }, new Options(), TestKeyringUnlocks.provider()));
|
||||
assertEquals(0,
|
||||
Tag.main(
|
||||
new String[] { "--type", "digest", "--mode", "verify", "--alg", "SHA-256", "--in",
|
||||
tagged.toString(), "--out", recovered.toString() },
|
||||
new Options(), TestKeyringUnlocks.provider()));
|
||||
|
||||
assertArrayEquals(pt, Files.readAllBytes(recovered), "digest round-trip mismatch");
|
||||
|
||||
@@ -223,15 +228,21 @@ public class TagTest {
|
||||
Files.write(plain, pt);
|
||||
|
||||
// produce
|
||||
assertEquals(0, Tag.main(new String[] { "--type", "digest", "--mode", "produce", "--alg", "SHA-256", "--in",
|
||||
plain.toString(), "--out", tagged.toString() }, new Options(), TestKeyringUnlocks.provider()));
|
||||
assertEquals(0,
|
||||
Tag.main(
|
||||
new String[] { "--type", "digest", "--mode", "produce", "--alg", "SHA-256", "--in",
|
||||
plain.toString(), "--out", tagged.toString() },
|
||||
new Options(), TestKeyringUnlocks.provider()));
|
||||
|
||||
// corrupt last byte -> break digest
|
||||
flipLastByte(tagged);
|
||||
|
||||
// verify (mismatch): expect throw + default marker ("digest invalid")
|
||||
assertEquals(1, Tag.main(new String[] { "--type", "digest", "--mode", "verify", "--alg", "SHA-256", "--in",
|
||||
tagged.toString(), "--out", out.toString() }, new Options(), TestKeyringUnlocks.provider()));
|
||||
assertEquals(1,
|
||||
Tag.main(
|
||||
new String[] { "--type", "digest", "--mode", "verify", "--alg", "SHA-256", "--in",
|
||||
tagged.toString(), "--out", out.toString() },
|
||||
new Options(), TestKeyringUnlocks.provider()));
|
||||
|
||||
assertTrue(Files.notExists(out, LinkOption.NOFOLLOW_LINKS));
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ final class TestKeyringUnlocks {
|
||||
}
|
||||
|
||||
static KeyringUnlockProvider provider() {
|
||||
return () -> new KeyringPassword(
|
||||
new char[] { 't', 'e', 's', 't', '-', 'k', 'e', 'y', 'r', 'i', 'n', 'g' });
|
||||
return () -> new KeyringPassword(new char[] { 't', 'e', 's', 't', '-', 'k', 'e', 'y', 'r', 'i', 'n', 'g' });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user