use k256::elliptic_curve::sec1::ToEncodedPoint;
use k256::elliptic_curve::PrimeField;
use k256::{ProjectivePoint, Scalar};
use thiserror::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::utils::hex_to_point;
#[derive(Error, Debug)]
pub enum Error {
#[error("Hex decoding failed: {0}")]
HexDecode(#[from] hex::FromHexError),
#[error("Invalid private key format: {0}")]
PrivateKeyFormat(String),
#[error("Invalid public key format: {0}")]
PublicKeyFormat(String),
#[error("Invalid scalar encoding (>= curve order N)")]
InvalidScalarEncoding,
#[error("Secp256k1 curve error: {0}")]
Secp256k1(#[from] k256::elliptic_curve::Error),
#[error("Ring must have at least 2 members, got {0}")]
RingTooSmall(usize),
#[error("Signer's public key (or its negation) not found in the ring")]
SignerNotInRing,
#[error("Signature verification failed (internal calculation mismatch)")]
VerificationFailed,
#[error("Invalid signature format (e.g., incorrect number of 's' values)")]
InvalidSignatureFormat,
#[error("Hashing error: {0}")]
HashingError(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RingSignature {
pub c0: String,
pub s: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RingSignatureBinary {
pub c0: Scalar,
pub s: Vec<Scalar>,
}
#[derive(Debug, Clone)]
pub struct KeyPair {
pub private_key: Scalar,
pub public_key: ProjectivePoint,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct KeyPairHex {
pub private_key_hex: String,
pub public_key_hex: String,
}
impl From<&RingSignatureBinary> for RingSignature {
fn from(binary: &RingSignatureBinary) -> Self {
RingSignature {
c0: scalar_to_hex(&binary.c0),
s: binary.s.iter().map(scalar_to_hex).collect(),
}
}
}
impl TryFrom<&RingSignature> for RingSignatureBinary {
type Error = Error;
fn try_from(sig: &RingSignature) -> Result<Self, Self::Error> {
let c0 = hex_to_scalar(&sig.c0)?;
let s = sig
.s
.iter()
.map(|s_hex| hex_to_scalar(s_hex))
.collect::<Result<Vec<Scalar>, Error>>()?;
Ok(RingSignatureBinary { c0, s })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct KeyImage(
#[cfg_attr(
feature = "serde",
serde(with = "k256::elliptic_curve::serde::ProjectivePoint")
)] pub ProjectivePoint,
);
impl KeyImage {
pub fn as_point(&self) -> &ProjectivePoint {
&self.0
}
pub fn from_point(point: ProjectivePoint) -> Self {
KeyImage(point)
}
pub fn to_hex(&self) -> String {
hex::encode(self.0.to_encoded_point(true).as_bytes())
}
pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
let point = hex_to_point(hex_str)?; Ok(KeyImage(point))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlsagSignatureBinary {
pub c0: Scalar,
pub s: Vec<Scalar>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BlsagSignature {
pub c0: String,
pub s: Vec<String>,
}
impl From<&BlsagSignatureBinary> for BlsagSignature {
fn from(binary: &BlsagSignatureBinary) -> Self {
BlsagSignature {
c0: scalar_to_hex(&binary.c0),
s: binary.s.iter().map(scalar_to_hex).collect(),
}
}
}
impl TryFrom<&BlsagSignature> for BlsagSignatureBinary {
type Error = Error;
fn try_from(sig: &BlsagSignature) -> Result<Self, Self::Error> {
let c0 = hex_to_scalar(&sig.c0)?;
let s = sig
.s
.iter()
.map(|s_hex| hex_to_scalar(s_hex))
.collect::<Result<Vec<Scalar>, Error>>()?;
Ok(BlsagSignatureBinary { c0, s })
}
}
pub(crate) fn scalar_to_hex(scalar: &Scalar) -> String {
hex::encode(scalar.to_bytes().as_slice())
}
pub(crate) fn hex_to_scalar(hex_str: &str) -> Result<Scalar, Error> {
let hex_str = normalize_hex(hex_str)?;
let padded_hex = if hex_str.len() < 64 {
format!("{:0>64}", hex_str)
} else {
hex_str
};
if padded_hex.len() != 64 {
return Err(Error::PrivateKeyFormat(format!(
"Hex len {} != 64",
padded_hex.len()
)));
}
let bytes = hex::decode(&padded_hex)?;
let field_bytes = k256::FieldBytes::from_slice(&bytes);
let maybe_scalar = Scalar::from_repr_vartime(*field_bytes);
if let Some(scalar) = maybe_scalar {
Ok(scalar)
} else {
Err(Error::InvalidScalarEncoding)
}
}
pub(crate) fn normalize_hex(hex_str: &str) -> Result<String, Error> {
let lower = hex_str
.trim_start_matches("0x")
.trim_start_matches("0X")
.to_lowercase();
if lower.chars().any(|c| !c.is_ascii_hexdigit()) {
return Err(Error::PublicKeyFormat(format!(
"Non-hex characters found: {}",
hex_str
)));
}
Ok(lower)
}