c5_core 1.0.1

Core utilities for c5store secret management CLI, providing cryptographic operations, key management, YAML parsing, and file I/O.
Documentation
# `c5_core` API Reference

The `c5_core` crate provides foundational utilities for cryptographic operations, key management, YAML manipulation, and file I/O, primarily designed to support the `c5cli` tool and `c5store` secret management.

## Error Type

All fallible functions in this crate return `Result<T, C5CoreError>`.

*   **`C5CoreError`**: An enum representing various errors that can occur.
    *   `Io(std::io::Error)`: General I/O error.
    *   `IoWithPath { path: PathBuf, source: std::io::Error }`: I/O error with path context.
    *   `PemParse(String)`: Error parsing PEM data.
    *   `KeyLoad(String)`: General error loading a key.
    *   `EciesOperation(ecies_25519::Error)`: Error during ECIES encryption/decryption.
    *   `EciesKeyParse(ecies_25519::KeyParsingError)`: Error parsing an ECIES key.
    *   `Base64Decode(base64::DecodeError)`: Error decoding Base64 data.
    *   `YamlDeserialize(String)`: Error deserializing YAML string (e.g., `YamlLoader::load_from_str`).
    *   `YamlSerialize(String)`: Error serializing YAML to string (e.g., `YamlEmitter::dump`).
    *   `YamlNavigation(String)`: Error navigating or manipulating YAML structure.
    *   `YamlRust2Parse(yaml_rust2::ScanError)`: Lower-level YAML parsing error from `yaml-rust2`.
    *   `UnsupportedAlgorithm(String)`: Algorithm not supported.
    *   `FileExists(PathBuf)`: Attempted to write to a file that already exists without force.
    *   `Encoding(String)`: Text encoding/decoding error.
    *   `InvalidInput(String)`: Invalid input provided to a function.

## Cryptographic Operations (`crypto_ops.rs`)

*   **Types:**
    *   `CryptoAlgorithm`: Enum for c5store crypto algorithms.
        *   `EciesX25519`
    *   `EciesPublicKey` (re-export of `ecies_25519::PublicKey`)
    *   `EciesStaticSecret` (re-export of `ecies_25519::StaticSecret`)

*   **Functions:**
    *   `encrypt_data(plaintext: &[u8], public_key: &EciesPublicKey, algo: CryptoAlgorithm, rng: &mut (impl RngCore + CryptoRng)) -> Result<Vec<u8>, C5CoreError>`
        *   Encrypts `plaintext` using the given ECIES `public_key` and `algo`.
        *   Requires a cryptographically secure random number generator `rng`.
    *   `decrypt_data(ciphertext: &[u8], private_key: &EciesStaticSecret, algo: CryptoAlgorithm) -> Result<Vec<u8>, C5CoreError>`
        *   Decrypts `ciphertext` using the given ECIES `private_key` and `algo`.

## Key Management (`keys.rs`)

*   **Types:**
    *   `PemEncodedKey(String)`: Wrapper for a PEM-encoded key string.
    *   `KeyPair { public: PemEncodedKey, private: PemEncodedKey }`: Holds a PEM-encoded public/private key pair.
    *   `SshKeyAlgorithm`: Enum for SSH key algorithms.
        *   `Ed25519`
    *   `SshKeyPair { private_key_pem: PemEncodedKey, public_key_openssh_format: String }`: Holds an SSH key pair.

*   **Functions:**
    *   `generate_c5_keypair(algo: CryptoAlgorithm, rng: &mut (impl RngCore + CryptoRng)) -> Result<KeyPair, C5CoreError>`
        *   Generates an ECIES key pair for c5store usage. Returns PEM-encoded keys.
    *   `generate_ssh_keypair(algo: SshKeyAlgorithm, comment_opt: Option<&str>) -> Result<SshKeyPair, C5CoreError>`
        *   Generates an SSH key pair (currently Ed25519).
        *   Private key is PEM-encoded PKCS#8. Public key is in OpenSSH format.
    *   `load_ecies_public_key(key_path: &Path) -> Result<EciesPublicKey, C5CoreError>`
        *   Loads an ECIES public key from a PEM file at `key_path`.
    *   `load_ecies_private_key(key_path: &Path) -> Result<EciesStaticSecret, C5CoreError>`
        *   Loads an ECIES private key from a PEM file at `key_path`.

## I/O Utilities (`io_utils.rs`)

*   **Functions:**
    *   `bytes_to_base64_string(data: &[u8]) -> String`
        *   Encodes byte slice to a Base64 string.
    *   `base64_string_to_bytes(s: &str) -> Result<Vec<u8>, C5CoreError>`
        *   Decodes a Base64 string to bytes.
    *   `read_file_to_bytes(file_path: &Path) -> Result<Vec<u8>, C5CoreError>`
        *   Reads entire file content into a byte vector.
    *   `read_file_to_string(file_path: &Path, encoding_name: &str) -> Result<String, C5CoreError>`
        *   Reads entire file content into a String. Currently, `encoding_name` must be "utf-8" (case-insensitive).
    *   `write_bytes_to_file(file_path: &Path, data: &[u8], force_overwrite: bool) -> Result<(), C5CoreError>`
        *   Writes byte slice to a file. If `force_overwrite` is false and file exists, returns `C5CoreError::FileExists`.
    *   `write_string_to_file(file_path: &Path, content: &str, force_overwrite: bool) -> Result<(), C5CoreError>`
        *   Writes string content (assumed UTF-8) to a file. If `force_overwrite` is false and file exists, returns `C5CoreError::FileExists`.

## c5store Secret Formatting (`secrets_format.rs`)

This module deals with the standard array format for c5store secrets within YAML.
`[<algorithm_string>, <key_name_string>, <base64_ciphertext_string>]`

*   **Types:**
    *   `C5SecretValueParts { algo_str: String, key_name: String, b64_ciphertext: String }`: Struct representing the parts of a c5store secret array.

*   **Functions:**
    *   `format_c5_secret_array(algo: CryptoAlgorithm, public_key_file_name: &str, b64_ciphertext: String) -> Result<yaml_rust2::Yaml, C5CoreError>`
        *   Formats the components into a `yaml_rust2::Yaml::Array`.
        *   Derives `key_name` from `public_key_file_name` (e.g., "my.key.pub.pem" -> "my.key").
    *   `parse_c5_secret_array(secret_yaml_value: &yaml_rust2::Yaml) -> Result<C5SecretValueParts, C5CoreError>`
        *   Parses a `yaml_rust2::Yaml::Array` into `C5SecretValueParts`.

## YAML Utilities (`yaml_utils.rs`)

Uses `yaml_rust2` for YAML processing.

*   **Types:**
    *   `yaml_rust2::Yaml`: The primary enum representing YAML values.

*   **Functions:**
    *   `load_yaml_from_string(yaml_str: &str) -> Result<Yaml, C5CoreError>`
        *   Loads the first YAML document from a string. Returns an empty `Yaml::Hash` for empty string.
    *   `dump_yaml_to_string(yaml_doc: &Yaml) -> Result<String, C5CoreError>`
        *   Serializes a `Yaml` document to a string.
    *   `get_yaml_value_at_path<'a>(root: &'a Yaml, path_str: &str) -> Option<&'a Yaml>`
        *   Retrieves a reference to a YAML value at a dot-separated `path_str` (e.g., "level1.level2.key").
        *   Returns `Some(root)` if `path_str` is empty.
    *   `set_yaml_value_at_path(root: &mut Yaml, path_str: &str, value_to_set: Yaml) -> Result<(), C5CoreError>`
        *   Sets a YAML value at a dot-separated `path_str`.
        *   Creates intermediate `Yaml::Hash` (map) nodes if they don't exist (or are `Yaml::Null`).
        *   If `path_str` is empty, replaces the `root` with `value_to_set`.
        *   Returns an error if an intermediate path segment is a scalar or array that cannot be converted to a map.