use secp::{MaybePoint, MaybeScalar, Point, Scalar, G};
use crate::errors::DecodeError;
use crate::BinaryEncoding;
pub const SCHNORR_SIGNATURE_SIZE: usize = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CompactSignature {
pub rx: [u8; 32],
pub s: MaybeScalar,
}
impl CompactSignature {
pub fn new(R: impl Into<Point>, s: impl Into<MaybeScalar>) -> CompactSignature {
CompactSignature {
rx: R.into().serialize_xonly(),
s: s.into(),
}
}
pub fn lift_nonce(&self) -> Result<LiftedSignature, secp::errors::InvalidPointBytes> {
let R = Point::lift_x(self.rx)?;
Ok(LiftedSignature { R, s: self.s })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LiftedSignature {
pub(crate) R: Point,
pub(crate) s: MaybeScalar,
}
impl LiftedSignature {
pub fn new(R: impl Into<Point>, s: impl Into<MaybeScalar>) -> LiftedSignature {
LiftedSignature {
R: R.into().to_even_y(),
s: s.into(),
}
}
pub fn compact(&self) -> CompactSignature {
CompactSignature::new(self.R, self.s)
}
pub fn encrypt(&self, adaptor_secret: impl Into<Scalar>) -> AdaptorSignature {
AdaptorSignature::new(self.R, self.s).encrypt(adaptor_secret)
}
pub fn unzip<P, S>(&self) -> (P, S)
where
P: From<Point>,
S: From<MaybeScalar>,
{
(P::from(self.R), S::from(self.s))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AdaptorSignature {
pub(crate) R: MaybePoint,
pub(crate) s: MaybeScalar,
}
impl AdaptorSignature {
pub fn new(R: impl Into<MaybePoint>, s: impl Into<MaybeScalar>) -> AdaptorSignature {
AdaptorSignature {
R: R.into(),
s: s.into(),
}
}
pub fn adapt<T: From<LiftedSignature>>(
&self,
adaptor_secret: impl Into<MaybeScalar>,
) -> Option<T> {
let adaptor_secret: MaybeScalar = adaptor_secret.into();
let adapted_nonce = (self.R + adaptor_secret * G).into_option()?;
let adapted_sig = self.s + adaptor_secret.negate_if(adapted_nonce.parity());
Some(T::from(LiftedSignature::new(adapted_nonce, adapted_sig)))
}
pub fn encrypt(&self, adaptor_secret: impl Into<Scalar>) -> AdaptorSignature {
let adaptor_secret: Scalar = adaptor_secret.into();
AdaptorSignature {
R: self.R - adaptor_secret * G,
s: self.s - adaptor_secret,
}
}
pub fn reveal_secret<S>(&self, final_sig: &LiftedSignature) -> Option<S>
where
S: From<MaybeScalar>,
{
let t = final_sig.s - self.s;
let T = t * G;
if T == final_sig.R - self.R {
Some(S::from(t))
} else if T == final_sig.R + self.R {
Some(S::from(-t))
} else {
None
}
}
pub fn unzip<P, S>(&self) -> (P, S)
where
P: From<MaybePoint>,
S: From<MaybeScalar>,
{
(P::from(self.R), S::from(self.s))
}
}
mod encodings {
use super::*;
impl BinaryEncoding for CompactSignature {
type Serialized = [u8; SCHNORR_SIGNATURE_SIZE];
fn to_bytes(&self) -> Self::Serialized {
let mut serialized = [0u8; SCHNORR_SIGNATURE_SIZE];
serialized[..SCHNORR_SIGNATURE_SIZE / 2].clone_from_slice(&self.rx);
serialized[SCHNORR_SIGNATURE_SIZE / 2..].clone_from_slice(&self.s.serialize());
serialized
}
fn from_bytes(signature_bytes: &[u8]) -> Result<Self, DecodeError<Self>> {
if signature_bytes.len() != SCHNORR_SIGNATURE_SIZE {
return Err(DecodeError::bad_length(signature_bytes.len()));
}
let rx = <[u8; 32]>::try_from(&signature_bytes[..32]).unwrap();
let s = MaybeScalar::try_from(&signature_bytes[32..])?;
Ok(CompactSignature { rx, s })
}
}
impl BinaryEncoding for LiftedSignature {
type Serialized = [u8; SCHNORR_SIGNATURE_SIZE];
fn to_bytes(&self) -> Self::Serialized {
CompactSignature::from(*self).to_bytes()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError<Self>> {
let compact_signature = CompactSignature::from_bytes(bytes).map_err(|e| e.convert())?;
Ok(compact_signature.lift_nonce()?)
}
}
impl BinaryEncoding for AdaptorSignature {
type Serialized = [u8; 65];
fn to_bytes(&self) -> Self::Serialized {
let mut serialized = [0u8; 65];
serialized[..33].clone_from_slice(&self.R.serialize());
serialized[33..].clone_from_slice(&self.s.serialize());
serialized
}
fn from_bytes(signature_bytes: &[u8]) -> Result<Self, DecodeError<Self>> {
if signature_bytes.len() != 65 {
return Err(DecodeError::bad_length(signature_bytes.len()));
}
let R = MaybePoint::try_from(&signature_bytes[..33])?;
let s = MaybeScalar::try_from(&signature_bytes[33..])?;
Ok(AdaptorSignature { R, s })
}
}
impl_encoding_traits!(CompactSignature, SCHNORR_SIGNATURE_SIZE);
impl_encoding_traits!(LiftedSignature, SCHNORR_SIGNATURE_SIZE);
impl_encoding_traits!(AdaptorSignature, 65);
impl_hex_display!(CompactSignature);
impl_hex_display!(LiftedSignature);
impl_hex_display!(AdaptorSignature);
}
mod internal_conversions {
use super::*;
impl TryFrom<CompactSignature> for LiftedSignature {
type Error = secp::errors::InvalidPointBytes;
fn try_from(signature: CompactSignature) -> Result<Self, Self::Error> {
signature.lift_nonce()
}
}
impl From<LiftedSignature> for CompactSignature {
fn from(signature: LiftedSignature) -> Self {
signature.compact()
}
}
}
#[cfg(feature = "secp256k1")]
mod secp256k1_conversions {
use super::*;
impl TryFrom<secp256k1::schnorr::Signature> for CompactSignature {
type Error = DecodeError<Self>;
fn try_from(signature: secp256k1::schnorr::Signature) -> Result<Self, Self::Error> {
Self::try_from(signature.to_byte_array())
}
}
impl TryFrom<secp256k1::schnorr::Signature> for LiftedSignature {
type Error = DecodeError<Self>;
fn try_from(signature: secp256k1::schnorr::Signature) -> Result<Self, Self::Error> {
Self::try_from(signature.to_byte_array())
}
}
impl From<CompactSignature> for secp256k1::schnorr::Signature {
fn from(signature: CompactSignature) -> Self {
Self::from_byte_array(signature.to_bytes())
}
}
impl From<LiftedSignature> for secp256k1::schnorr::Signature {
fn from(signature: LiftedSignature) -> Self {
Self::from_byte_array(signature.to_bytes())
}
}
}
#[cfg(feature = "k256")]
mod k256_conversions {
use super::*;
impl From<(k256::PublicKey, k256::Scalar)> for CompactSignature {
fn from((R, s): (k256::PublicKey, k256::Scalar)) -> Self {
CompactSignature::new(R, s)
}
}
impl From<(k256::PublicKey, k256::Scalar)> for LiftedSignature {
fn from((R, s): (k256::PublicKey, k256::Scalar)) -> Self {
LiftedSignature::new(R, s)
}
}
impl TryFrom<CompactSignature> for (k256::PublicKey, k256::Scalar) {
type Error = secp::errors::InvalidPointBytes;
fn try_from(signature: CompactSignature) -> Result<Self, Self::Error> {
Ok(signature.lift_nonce()?.unzip())
}
}
impl From<LiftedSignature> for (k256::PublicKey, k256::Scalar) {
fn from(signature: LiftedSignature) -> Self {
signature.unzip()
}
}
impl From<LiftedSignature> for (k256::AffinePoint, k256::Scalar) {
fn from(signature: LiftedSignature) -> Self {
signature.unzip()
}
}
#[cfg(feature = "k256")]
impl From<CompactSignature> for k256::WideBytes {
fn from(signature: CompactSignature) -> Self {
<[u8; SCHNORR_SIGNATURE_SIZE]>::from(signature).into()
}
}
#[cfg(feature = "k256")]
impl From<LiftedSignature> for k256::WideBytes {
fn from(signature: LiftedSignature) -> Self {
<[u8; SCHNORR_SIGNATURE_SIZE]>::from(signature).into()
}
}
}