use reallyme_crypto::core::Algorithm as CryptoAlgorithm;
use reallyme_crypto::dispatch::{sign as dispatch_sign, verify as dispatch_verify};
use thiserror::Error;
use crate::jws::{
parse_compact::{build_sig_structure, parse_compact_jws},
parse_header::JwsAlgorithm,
sign::{encode_compact_jws, encode_jws_signing_input, JwsSigningInputError},
verify::{decode_and_validate_header, decode_signature},
};
const ED25519_SIGNATURE_LEN: usize = 64;
#[derive(Debug, Clone, Copy, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum JwsEddsaError {
#[error("EdDSA JWS signing failed")]
SignFailed,
#[error("EdDSA JWS header is not valid base64url")]
BadHeaderBase64,
#[error("EdDSA JWS header is not valid UTF-8")]
BadHeaderUtf8,
#[error("EdDSA JWS signature is not valid base64url")]
BadSignatureBase64,
#[error("EdDSA JWS compact serialization is invalid")]
InvalidCompactEncoding,
#[error("EdDSA JWS signing input length overflow")]
LengthOverflow,
#[error("EdDSA JWS compact serialization is too large")]
InputTooLarge,
#[error("EdDSA JWS header does not bind to alg EdDSA")]
HeaderMismatch,
#[error("EdDSA JWS signature is invalid")]
InvalidSignature,
}
pub fn sign_eddsa_jws(secret_key: &[u8], payload_text: &str) -> Result<String, JwsEddsaError> {
let signing_input = encode_jws_signing_input(JwsAlgorithm::Eddsa, payload_text.as_bytes())
.map_err(JwsEddsaError::from)?;
let signature = dispatch_sign(
CryptoAlgorithm::Ed25519,
secret_key,
&signing_input.signing_input,
)
.map_err(|_| JwsEddsaError::SignFailed)?;
encode_compact_jws(signing_input, &signature).map_err(JwsEddsaError::from)
}
pub fn verify_eddsa_jws(jws: &str, public_key: &[u8]) -> Result<(), JwsEddsaError> {
let parts = parse_compact_jws(jws, JwsEddsaError::InvalidCompactEncoding)?;
decode_and_validate_header(
parts.protected_header,
JwsAlgorithm::Eddsa,
JwsEddsaError::BadHeaderBase64,
JwsEddsaError::BadHeaderUtf8,
JwsEddsaError::HeaderMismatch,
)?;
let signing_input = build_sig_structure(
parts.protected_header,
parts.payload,
JwsEddsaError::LengthOverflow,
)?;
let signature = decode_signature(parts.signature, JwsEddsaError::BadSignatureBase64)?;
if signature.len() != ED25519_SIGNATURE_LEN {
return Err(JwsEddsaError::InvalidSignature);
}
dispatch_verify(
CryptoAlgorithm::Ed25519,
public_key,
&signing_input,
&signature,
)
.map_err(|_| JwsEddsaError::InvalidSignature)
}
impl From<JwsSigningInputError> for JwsEddsaError {
fn from(error: JwsSigningInputError) -> Self {
match error {
JwsSigningInputError::LengthOverflow => JwsEddsaError::LengthOverflow,
JwsSigningInputError::InputTooLarge => JwsEddsaError::InputTooLarge,
}
}
}