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::{parse_compact_jws, signing_input},
parse_header::JwsAlgorithm,
sign::{encode_compact_jws, encode_jws_signing_input},
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)]
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 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());
let raw64 = sign_p256_jose_prehash(secret_key, signing_input.signing_input.as_bytes())?;
Ok(encode_compact_jws(signing_input, raw64.as_ref()))
}
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 = signing_input(parts.protected_header, parts.payload);
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.as_bytes(), public_key_sec1)
}