Skip to content

Conversation

@kiyoaki
Copy link
Member

@kiyoaki kiyoaki commented Feb 11, 2026

Summary

  • Change --format from OutputFormat (default hex) to Option<OutputFormat> across all 5 commands
  • When --format is explicitly specified, decode_input uses that format directly without fallback, preventing misdetection (e.g., base64 data that looks like valid hex)
  • When --format is omitted, auto-detection (PEM -> hex -> base64) is preserved for backward compatibility
  • Output encoding defaults to hex when --format is not specified
  • Add 12 unit tests for decode_input covering explicit format, fallback prevention, and auto-detect modes
  • Add 3 integration tests: explicit base64 roundtrip, format mismatch error messages, default hex output

Implements [M-3] from PLANS.md.

Test plan

  • cargo fmt --all -- --check passes
  • cargo clippy --workspace --all-features -- -D warnings passes
  • cargo clippy --workspace --no-default-features -- -D warnings passes
  • cargo test --workspace --all-features passes (33 tests, 0 failures)
  • CI passes

Change --format from OutputFormat (default hex) to Option<OutputFormat>.
When explicitly specified, decode_input uses that format directly without
fallback, preventing misdetection of base64 data as hex. When omitted,
auto-detection (PEM -> hex -> base64) is preserved for backward
compatibility. Output defaults to hex when --format is not specified.

Add unit tests for decode_input with explicit/auto-detect modes and
integration tests for explicit format roundtrip, format mismatch error
messages, and default hex output verification.
Copilot AI review requested due to automatic review settings February 11, 2026 09:44
@gemini-code-assist
Copy link

Summary of Changes

Hello @kiyoaki, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the CLI's robustness and user control over data encoding and decoding. By introducing an explicit --format option for input, it eliminates ambiguity and potential misinterpretations of data that could be valid in multiple formats (e.g., hex vs. base64). This change improves the reliability of cryptographic operations by ensuring data is processed exactly as intended, while still maintaining a convenient auto-detection mechanism for cases where the format is not explicitly provided.

Highlights

  • Input Format Handling: The --format option has been updated across all five CLI commands to accept an Option<OutputFormat>, allowing for explicit format specification or auto-detection.
  • Explicit Format Behavior: When --format is explicitly provided, the input is decoded using the specified format directly, preventing misdetection and fallback to other formats.
  • Auto-detection Preservation: If the --format option is omitted, the existing auto-detection logic (PEM -> hex -> base64) is preserved for backward compatibility.
  • Default Output Format: Output encoding now defaults to hexadecimal when no --format is specified.
  • Testing: Twelve new unit tests were added for the decode_input function, covering explicit format, fallback prevention, and auto-detection modes. Additionally, three new integration tests were introduced for explicit base64 roundtrip, format mismatch error messages, and default hex output.
Changelog
  • PLANS.md
    • Updated the [M-3] entry to reflect the new input format disambiguation approach.
    • Removed an outdated [M-3] entry related to input format improvements.
  • kylix-cli/src/cli.rs
    • Modified the --format argument for all commands to be an Option<OutputFormat> instead of a mandatory OutputFormat.
    • Updated the help text for the --format option to clarify its behavior for input (auto-detect) and output (hex default).
  • kylix-cli/src/commands/decaps.rs
    • Updated the cmd_decaps function signature to accept Option<OutputFormat> for the format parameter.
    • Introduced out_format variable to default to OutputFormat::Hex if format is None for output encoding.
  • kylix-cli/src/commands/encaps.rs
    • Updated the cmd_encaps function signature to accept Option<OutputFormat> for the format parameter.
    • Introduced out_format variable to default to OutputFormat::Hex if format is None for output encoding.
  • kylix-cli/src/commands/keygen.rs
    • Updated the cmd_keygen function signature to accept Option<OutputFormat> for the format parameter.
    • Introduced out_format variable to default to OutputFormat::Hex if format is None for output encoding.
  • kylix-cli/src/commands/sign.rs
    • Updated the cmd_sign function signature to accept Option<OutputFormat> for the format parameter.
    • Introduced out_format variable to default to OutputFormat::Hex if format is None for output encoding.
  • kylix-cli/src/commands/verify.rs
    • Updated the cmd_verify function signature to accept Option<OutputFormat> for the format parameter.
  • kylix-cli/src/io.rs
    • Refactored the decode_input function to handle Option<OutputFormat>, enabling explicit format decoding or auto-detection.
    • Extracted PEM decoding logic into a new private helper function decode_pem.
    • Added detailed error messages for explicit format decoding failures.
    • Implemented comprehensive unit tests for decode_input covering explicit hex, base64, PEM, explicit format preventing fallback, and auto-detection scenarios.
    • Added unit tests for encode_output roundtrips for hex, base64, and PEM.
  • kylix-cli/tests/cli_integration.rs
    • Added an integration test test_explicit_base64_roundtrip to verify correct handling of base64 input/output when explicitly specified.
    • Added an integration test test_format_mismatch_error to ensure helpful error messages are displayed when an explicit format does not match the input.
    • Added an integration test test_default_output_is_hex to confirm that output defaults to hexadecimal when no format is specified.
Activity
  • The author confirmed that cargo fmt --all -- --check passes.
  • The author confirmed that cargo clippy --workspace --all-features -- -D warnings passes.
  • The author confirmed that cargo clippy --workspace --no-default-features -- -D warnings passes.
  • The author confirmed that cargo test --workspace --all-features passes (33 tests, 0 failures).
  • The author indicated that CI passes is pending.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the CLI to treat --format as an explicit input encoding override (when provided), while preserving auto-detection when omitted, to prevent ambiguous hex/base64 misdetection.

Changes:

  • Switched --format from OutputFormat (default hex) to Option<OutputFormat> across commands and decoding logic.
  • Updated decode_input to honor explicit format without fallback, preserving auto-detect only when --format is omitted.
  • Added unit + integration tests covering explicit format behavior, mismatch errors, and default hex output.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
kylix-cli/src/io.rs Implements explicit-vs-auto decode behavior; adds decode helpers and unit tests.
kylix-cli/src/cli.rs Makes --format optional and updates help text across commands.
kylix-cli/src/commands/keygen.rs Defaults output encoding to hex when --format is omitted.
kylix-cli/src/commands/encaps.rs Defaults ciphertext/shared-secret output encoding to hex when --format is omitted.
kylix-cli/src/commands/decaps.rs Defaults shared-secret output encoding to hex when --format is omitted.
kylix-cli/src/commands/sign.rs Defaults signature output encoding to hex when --format is omitted.
kylix-cli/src/commands/verify.rs Updates command signature to accept optional format.
kylix-cli/tests/cli_integration.rs Adds integration tests for explicit format roundtrip, mismatch errors, and default hex output.
PLANS.md Marks the M-3 plan item as implemented with the new behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link

External Tool Benchmark (arm64-macos)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 28.0 µs 25.0 µs 29.0 µs
liboqs 21.5 µs 18.1 µs 16.5 µs

@github-actions
Copy link

External Tool Benchmark (x86_64-linux)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 62.0 µs 53.0 µs 45.0 µs
liboqs 19.6 µs 16.5 µs 16.1 µs

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a great improvement, introducing an explicit --format option to disambiguate input formats and prevent auto-detection errors. The change from OutputFormat to Option<OutputFormat> is applied consistently across all relevant commands. The addition of comprehensive unit and integration tests significantly improves the robustness of the input decoding logic. I have a couple of suggestions to further improve the new PEM decoding logic and Rust code style.

- Validate -----END footer in decode_pem (Copilot + Gemini)
- Use generic error message listing all formats: hex|base64|pem (Copilot)
- Refactor None branch to if/else if/else expression (Gemini)
- Use &Path instead of to_str().unwrap() in new integration tests (Copilot)
- Use input-only help text for verify's --format arg (Copilot)
@github-actions
Copy link

External Tool Benchmark (arm64-macos)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 28.0 µs 25.0 µs 26.0 µs
liboqs 19.1 µs 19.1 µs 16.4 µs

@github-actions
Copy link

External Tool Benchmark (x86_64-linux)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 62.0 µs 53.0 µs 44.0 µs
liboqs 19.6 µs 16.4 µs 16.1 µs

- Extract OutputFormat::DEFAULT constant to avoid repeating Hex default
  across 4 command files (keygen, encaps, decaps, sign)
- Add [L-6] PEM Label Validation to PLANS.md for future BEGIN/END
  label mismatch detection in decode_pem
Copilot AI review requested due to automatic review settings February 11, 2026 14:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link

External Tool Benchmark (arm64-macos)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 38.0 µs 41.0 µs 49.0 µs
liboqs 18.5 µs 20.7 µs 23.6 µs

@github-actions
Copy link

External Tool Benchmark (x86_64-linux)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 62.0 µs 53.0 µs 44.0 µs
liboqs 19.5 µs 16.4 µs 16.1 µs

- Handle CRLF line endings in decode_pem by normalizing \r (Copilot)
- Replace OutputFormat::DEFAULT with #[derive(Default)] + #[default]
  and use unwrap_or_default() for idiomatic Rust (Copilot)
- Fix test_default_output_is_hex: add empty check and use
  floor_char_boundary for safe string slicing (Copilot)
@kiyoaki kiyoaki requested a review from Copilot February 11, 2026 14:22
@kiyoaki
Copy link
Member Author

kiyoaki commented Feb 11, 2026

/gemini review

@github-actions
Copy link

External Tool Benchmark (arm64-macos)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 28.0 µs 25.0 µs 28.0 µs
liboqs 24.9 µs 26.1 µs 28.0 µs

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link

External Tool Benchmark (x86_64-linux)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 63.0 µs 53.0 µs 44.0 µs
liboqs 19.5 µs 16.5 µs 16.1 µs

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant improvement by disambiguating input formats through an explicit --format option. The changes correctly transition the format argument to Option<OutputFormat> across all relevant commands, allowing users to specify the encoding or rely on robust auto-detection. The decode_input function has been refactored to prioritize explicit format choices and provides helpful error messages when a format mismatch occurs. The addition of a Pem variant to OutputFormat and setting Hex as the default for output when unspecified are well-implemented. The comprehensive suite of unit and integration tests thoroughly validates the new behavior, covering explicit format decoding, fallback prevention, and auto-detection scenarios. Overall, this is a well-executed feature that enhances usability and clarity for input handling.

- Remove line continuation in error messages to avoid embedded
  indentation spaces in output (Copilot)
- Simplify test prefix slice to trimmed.get(..40).unwrap_or(trimmed)
  instead of floor_char_boundary (Copilot)
@github-actions
Copy link

External Tool Benchmark (arm64-macos)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 37.0 µs 27.0 µs 27.0 µs
liboqs 29.7 µs 22.8 µs 19.2 µs

@github-actions
Copy link

External Tool Benchmark (x86_64-linux)

Kylix Benchmark Comparison

ML-KEM-768

Library decaps encaps keygen
Kylix 62.0 µs 53.0 µs 44.0 µs
liboqs 19.6 µs 16.5 µs 16.2 µs

@kiyoaki kiyoaki requested a review from Copilot February 11, 2026 15:27
@kiyoaki
Copy link
Member Author

kiyoaki commented Feb 11, 2026

/gemini review

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +52 to +57
if lines.len() < 3
|| !lines[0].starts_with("-----BEGIN")
|| !lines.last().unwrap().starts_with("-----END")
{
bail!("Invalid PEM format: expected -----BEGIN header, body lines, and -----END footer");
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PEM header/footer validation is permissive: it only checks starts_with and does not ensure the BEGIN/END labels match or that the footer line is well-formed. This can accept malformed PEM like -----END X-----garbage or mismatched labels. Consider parsing the label from the BEGIN line and requiring the END line to exactly match -----END {label}----- (and rejecting extra trailing characters/lines).

Suggested change
if lines.len() < 3
|| !lines[0].starts_with("-----BEGIN")
|| !lines.last().unwrap().starts_with("-----END")
{
bail!("Invalid PEM format: expected -----BEGIN header, body lines, and -----END footer");
}
if lines.len() < 3 {
bail!("Invalid PEM format: expected -----BEGIN header, body lines, and -----END footer");
}
// Validate and parse the BEGIN line: -----BEGIN <LABEL>-----
let begin_line = lines[0];
const BEGIN_PREFIX: &str = "-----BEGIN ";
const PEM_SUFFIX: &str = "-----";
if !begin_line.starts_with(BEGIN_PREFIX) || !begin_line.ends_with(PEM_SUFFIX) {
bail!("Invalid PEM header: expected line of the form '-----BEGIN <LABEL>-----'");
}
let label = &begin_line[BEGIN_PREFIX.len()..begin_line.len() - PEM_SUFFIX.len()];
if label.is_empty() {
bail!("Invalid PEM header: empty label");
}
// Validate END line exactly matches the parsed label: -----END <LABEL>-----
let end_line = lines.last().unwrap();
let expected_end = format!("-----END {}-----", label);
if end_line != &expected_end {
bail!("Invalid PEM footer: expected '{}'", expected_end);
}
// Join all body lines between BEGIN and END as base64 content

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +79
Some(OutputFormat::Hex) => hex::decode(data).context(
"Failed to decode as hex. If the input uses a different encoding, remove --format or set it to the encoding used by the input: hex|base64|pem.",
),
Some(OutputFormat::Base64) => BASE64.decode(data).context(
"Failed to decode as base64. If the input uses a different encoding, remove --format or set it to the encoding used by the input: hex|base64|pem.",
),
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hex/base64 branches duplicate a long, mostly identical guidance string. Consider extracting a small helper to format the message (e.g., based on the requested OutputFormat) to keep messaging consistent and make future edits less error-prone.

Copilot uses AI. Check for mistakes.
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a great improvement, successfully disambiguating input formats by making the --format flag optional. When provided, it strictly enforces the format, and when omitted, it maintains backward compatibility with auto-detection. The changes are consistently applied across all relevant commands. The addition of comprehensive unit and integration tests is excellent and covers the new logic well, including edge cases like hex-looking base64 data. I have a couple of minor suggestions for improving code clarity and error handling in io.rs.

Comment on lines +44 to +50
let normalized;
let data = if data.contains('\r') {
normalized = data.replace('\r', "");
&normalized
} else {
data
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better readability and to use a more idiomatic Rust pattern for conditional allocation, you could use std::borrow::Cow (Clone-on-Write) to handle the optional string normalization. This avoids the slightly complex lifetime management with the normalized variable and makes the intent clearer.

    let data: std::borrow::Cow<str> = if data.contains('\r') {
        std::borrow::Cow::Owned(data.replace('\r', ""))
    } else {
        std::borrow::Cow::Borrowed(data)
    };

} else if is_hex(data) && data.len() % 2 == 0 {
hex::decode(data).context("Failed to decode hex")
} else {
BASE64.decode(data).context("Failed to decode base64")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message here could be more helpful to the user. Since this is the last step in auto-detection, if it fails, it's likely the input is in an unsupported format or is malformed. A more descriptive error could guide the user to specify the format explicitly with --format if they know it.

                BASE64.decode(data).context("Failed to decode as base64. Auto-detection fell back to base64, but decoding failed. If you know the input format, try specifying it with --format.")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant