use reallyme_crypto::p256::{
p256_ecdsa_der_to_jose_signature, p256_ecdsa_jose_signature_to_der, sign_p256_der_prehash,
verify_p256_der_prehash, P256_ECDSA_JOSE_SIGNATURE_LEN,
};
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 ES256_JOSE_SIGNATURE_LEN: usize = P256_ECDSA_JOSE_SIGNATURE_LEN;
#[derive(Debug, Clone, Copy, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum JwsEs256Error {
#[error("ES256 JWS signing failed")]
SignFailed,
#[error("ES256 JWS signer returned an invalid DER signature")]
BadDerSignature,
#[error("ES256 JWS header is not valid base64url")]
BadHeaderBase64,
#[error("ES256 JWS header is not valid UTF-8")]
BadHeaderUtf8,
#[error("ES256 JWS signature is not valid base64url")]
BadSignatureBase64,
#[error("ES256 JWS signature is not valid raw P-256 JOSE form")]
BadRawSignature,
#[error("ES256 JWS compact serialization is invalid")]
InvalidCompactEncoding,
#[error("ES256 JWS signing input length overflow")]
LengthOverflow,
#[error("ES256 JWS compact serialization is too large")]
InputTooLarge,
#[error("ES256 JWS header does not bind to alg ES256")]
HeaderMismatch,
#[error("ES256 JWS signature is invalid")]
InvalidSignature,
#[error("ES256 JWS verification failed")]
VerifyFailed,
}
pub fn sign_p256_jose_prehash(
secret_key: &[u8],
signing_input: &[u8],
) -> Result<[u8; ES256_JOSE_SIGNATURE_LEN], JwsEs256Error> {
let der_sig =
sign_p256_der_prehash(secret_key, signing_input).map_err(|_| JwsEs256Error::SignFailed)?;
p256_ecdsa_der_to_jose_signature(&der_sig).map_err(|_| JwsEs256Error::BadDerSignature)
}
pub fn verify_p256_jose_prehash(
signature: &[u8],
signing_input: &[u8],
public_key_sec1: &[u8],
) -> Result<(), JwsEs256Error> {
if signature.len() != ES256_JOSE_SIGNATURE_LEN {
return Err(JwsEs256Error::InvalidSignature);
}
let der =
p256_ecdsa_jose_signature_to_der(signature).map_err(|_| JwsEs256Error::BadRawSignature)?;
verify_p256_der_prehash(&der, signing_input, public_key_sec1)
.map_err(|_| JwsEs256Error::InvalidSignature)
}
pub fn sign_es256_jws(secret_key: &[u8], payload_text: &str) -> Result<String, JwsEs256Error> {
let signing_input = encode_jws_signing_input(JwsAlgorithm::Es256, payload_text.as_bytes())
.map_err(JwsEs256Error::from)?;
let raw64 = sign_p256_jose_prehash(secret_key, &signing_input.signing_input)?;
encode_compact_jws(signing_input, raw64.as_ref()).map_err(JwsEs256Error::from)
}
pub fn verify_es256_jws(jws: &str, public_key_sec1: &[u8]) -> Result<(), JwsEs256Error> {
let parts = parse_compact_jws(jws, JwsEs256Error::InvalidCompactEncoding)?;
decode_and_validate_header(
parts.protected_header,
JwsAlgorithm::Es256,
JwsEs256Error::BadHeaderBase64,
JwsEs256Error::BadHeaderUtf8,
JwsEs256Error::HeaderMismatch,
)?;
let signing_input = build_sig_structure(
parts.protected_header,
parts.payload,
JwsEs256Error::LengthOverflow,
)?;
let raw = decode_signature(parts.signature, JwsEs256Error::BadSignatureBase64)?;
if raw.len() != ES256_JOSE_SIGNATURE_LEN {
return Err(JwsEs256Error::InvalidSignature);
}
verify_p256_jose_prehash(&raw, &signing_input, public_key_sec1)
}
impl From<JwsSigningInputError> for JwsEs256Error {
fn from(error: JwsSigningInputError) -> Self {
match error {
JwsSigningInputError::LengthOverflow => JwsEs256Error::LengthOverflow,
JwsSigningInputError::InputTooLarge => JwsEs256Error::InputTooLarge,
}
}
}