jwk_kit 0.1.2

A Rust Library for JSON Web Keys (JWK)
Documentation
use base64::{
    Engine,
    engine::general_purpose::URL_SAFE_NO_PAD
};
use crate::error::JwkError;
use p256::{
    ecdsa::SigningKey,
    elliptic_curve::rand_core::OsRng,
    elliptic_curve::sec1::ToEncodedPoint,
    pkcs8::{DecodePublicKey, EncodePrivateKey, EncodePublicKey},
    PublicKey
};

/// Generates a new ES256 (P-256 curve) key pair and returns it in PEM format.
///
/// This function creates a fresh ECDSA key pair using the P-256 (aka prime256v1) elliptic curve,
/// which is used for the ES256 algorithm in JWT and other cryptographic contexts.
/// The resulting private and public keys are returned as PEM-encoded strings.
///
/// # Returns
/// A `Result` containing a tuple of `(private_key_pem, public_key_pem)` as `String`s on success,
/// or a `JwkError` on failure. The PEM strings can be used for further processing, 
/// such as conversion to JWK format or storing to files.
///
/// # Errors
/// Returns a `JwkError` if key generation fails or if the key cannot be serialized to PEM format.
///
/// # Example
/// ```rust
/// use jwk_kit::generator::ecdsa::generate_es256_keypair_pem;
/// match generate_es256_keypair_pem() {
///     Ok((private_pem, public_pem)) => {
///         println!("Private Key:\n{}", private_pem);
///         println!("Public Key:\n{}", public_pem);
///     },
///     Err(e) => {
///         eprintln!("Key generation failed: {:?}", e);
///     },
/// }
/// ```
///
/// # Note
/// The keys generated by this function can be used with `extract_es256_coordinates`
/// to produce a valid JWK representation of the public key.
pub fn generate_es256_keypair_pem() -> Result<(String, String), JwkError> {
    let signing_key = SigningKey::random(&mut OsRng);

    let private_pem = signing_key
        .to_pkcs8_pem(p256::pkcs8::LineEnding::LF)
        .map(|pem| pem.to_string())
        .map_err(|_| JwkError::KeyGenerationFailed)?;

    let verify_key = signing_key.verifying_key();

    let public_pem = verify_key
        .to_public_key_pem(p256::pkcs8::LineEnding::LF)
        .map(|pem| pem.to_string())
        .map_err(|_| JwkError::KeyGenerationFailed)?;

    Ok((private_pem, public_pem))
}

/// Extracts the EC coordinates (X and Y) from a PEM-encoded ES256 key.
///
/// This function takes a PEM-encoded EC key and extracts the X and Y coordinates
/// that are necessary for the ES256 (Elliptic Curve Digital Signature Algorithm) 
/// as part of the key's JWK representation.
///
/// # Parameters
/// - `pem_data`: A string slice that contains the PEM-encoded EC key (in either private or public key format).
///
/// # Returns
/// A `Result` that contains a tuple of the X and Y coordinates as `String`s in case of success, 
/// or a `JwkError` in case of failure. The coordinates are the base64url-encoded strings
/// representing the X and Y values of the elliptic curve point.
///
/// # Errors
/// This function returns a `JwkError` if:
/// - The input PEM data cannot be parsed as a valid EC key.
/// - The key is not in the expected ES256 format (P-256 curve).
/// - Any other error occurs while extracting the coordinates.
///
/// # Example
/// ```rust
/// use jwk_kit::generator::ecdsa::extract_es256_coordinates;
/// let pem_data = "-----BEGIN PUBLIC KEY-----\n...";  // PEM-encoded EC key
/// match extract_es256_coordinates(pem_data) {
///     Ok((x, y)) => {
///         println!("X: {}, Y: {}", x, y);
///     },
///     Err(e) => {
///         eprintln!("Failed to extract coordinates: {:?}", e);
///     },
/// }
/// ```
///
/// # Note
/// This function is typically used to extract the public key coordinates from an ES256 key
/// for use in a JWK format.
pub fn extract_es256_coordinates(pem_data: &str) -> Result<(String, String), JwkError> {
    let public_key = PublicKey::from_public_key_pem(pem_data)
        .map_err(|_| JwkError::MissingEcParams)?;

    let encoded_point = public_key.to_encoded_point(false);
    let x = encoded_point.x().ok_or(JwkError::MissingEcX)?;
    let y = encoded_point.y().ok_or(JwkError::MissingEcY)?;

    let x_b64 = URL_SAFE_NO_PAD.encode(x);
    let y_b64 = URL_SAFE_NO_PAD.encode(y);

    Ok((x_b64, y_b64))
}