cesrox 2.0.0-beta.7

Implementation of Composable Event Streaming Representation (CESR)
Documentation
use crate::{
    conversion::{adjust_with_num, b64_to_num},
    derivation_code::DerivationCode,
    error::Error,
    primitives::codes::self_signing::SelfSigning,
};
use core::str::FromStr;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Index {
    BothSame(u16),
    Dual(u16, u16),
    BigDual(u16, u16),
    CurrentOnly(u16),
    BigCurrentOnly(u16),
}
impl Index {
    pub fn current(&self) -> u16 {
        match self {
            Index::BothSame(i)
            | Index::Dual(i, _)
            | Index::BigDual(i, _)
            | Index::CurrentOnly(i)
            | Index::BigCurrentOnly(i) => *i,
        }
    }
    pub fn prev_next(&self) -> Option<u16> {
        match self {
            Index::BothSame(i) | Index::Dual(_, i) | Index::BigDual(_, i) => Some(*i),
            _ => None,
        }
    }
}
/// Attached Signature Derivation Codes
///
/// A self signing prefix derivation outputs a signature as its derivative (2.3.5)
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct AttachedSignatureCode {
    pub index: Index,
    pub code: SelfSigning,
}

impl AttachedSignatureCode {
    pub fn new(code: SelfSigning, index: Index) -> Self {
        Self { index, code }
    }

    pub fn new_from_ints(code: SelfSigning, current: u16, prev_next: Option<u16>) -> Self {
        let index = match prev_next {
            Some(i) => {
                if i == current {
                    Index::BothSame(i)
                } else {
                    Index::BigDual(current, i)
                }
            }
            None => Index::CurrentOnly(current),
        };
        Self { code, index }
    }
}

impl DerivationCode for AttachedSignatureCode {
    fn soft_size(&self) -> usize {
        match (self.code, self.index) {
            (SelfSigning::Ed25519Sha512, Index::BothSame(_)) => 1,
            (SelfSigning::Ed25519Sha512, Index::Dual(_, _))
            | (SelfSigning::Ed25519Sha512, Index::BigDual(_, _)) => 4,
            (SelfSigning::Ed25519Sha512, Index::CurrentOnly(_)) => 1,
            (SelfSigning::Ed25519Sha512, Index::BigCurrentOnly(_)) => 4,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BothSame(_)) => 1,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::Dual(_, _)) => 1,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BigDual(_, _)) => 4,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::CurrentOnly(_)) => 1,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BigCurrentOnly(_)) => 4,
            (SelfSigning::Ed448, Index::BothSame(_)) => 2,
            (SelfSigning::Ed448, Index::Dual(_, _)) => 2,
            (SelfSigning::Ed448, Index::BigDual(_, _)) => 6,
            (SelfSigning::Ed448, Index::CurrentOnly(_)) => 2,
            (SelfSigning::Ed448, Index::BigCurrentOnly(_)) => 6,
            (SelfSigning::ECDSA256r1Sha256, Index::BothSame(_)) => 1,
            (SelfSigning::ECDSA256r1Sha256, Index::Dual(_, _)) => 1,
            (SelfSigning::ECDSA256r1Sha256, Index::BigDual(_, _)) => 4,
            (SelfSigning::ECDSA256r1Sha256, Index::CurrentOnly(_)) => 1,
            (SelfSigning::ECDSA256r1Sha256, Index::BigCurrentOnly(_)) => 4,
        }
    }

    fn hard_size(&self) -> usize {
        match (self.code, self.index) {
            (SelfSigning::Ed25519Sha512, Index::BothSame(_)) => 1,
            (SelfSigning::Ed25519Sha512, Index::Dual(_, _)) => 1,
            (SelfSigning::Ed25519Sha512, Index::BigDual(_, _)) => 2,
            (SelfSigning::Ed25519Sha512, Index::CurrentOnly(_)) => 1,
            (SelfSigning::Ed25519Sha512, Index::BigCurrentOnly(_)) => 2,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BothSame(_)) => 1,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::Dual(_, _)) => 1,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BigDual(_, _)) => 2,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::CurrentOnly(_)) => 1,
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BigCurrentOnly(_)) => 2,
            (SelfSigning::Ed448, Index::BothSame(_)) => 2,
            (SelfSigning::Ed448, Index::Dual(_, _)) => 2,
            (SelfSigning::Ed448, Index::BigDual(_, _)) => 2,
            (SelfSigning::Ed448, Index::CurrentOnly(_)) => 2,
            (SelfSigning::Ed448, Index::BigCurrentOnly(_)) => 2,
            (SelfSigning::ECDSA256r1Sha256, Index::BothSame(_)) => 1,
            (SelfSigning::ECDSA256r1Sha256, Index::Dual(_, _)) => 1,
            (SelfSigning::ECDSA256r1Sha256, Index::BigDual(_, _)) => 2,
            (SelfSigning::ECDSA256r1Sha256, Index::CurrentOnly(_)) => 1,
            (SelfSigning::ECDSA256r1Sha256, Index::BigCurrentOnly(_)) => 2,
        }
    }

    fn value_size(&self) -> usize {
        match (self.code, self.index) {
            (SelfSigning::Ed25519Sha512, _) => 86,
            (SelfSigning::ECDSAsecp256k1Sha256, _) => 86,
            (SelfSigning::ECDSA256r1Sha256, _) => 86,
            (SelfSigning::Ed448, _) => 152,
        }
    }

    fn to_str(&self) -> String {
        let code = match (self.code, self.index) {
            (SelfSigning::Ed25519Sha512, Index::BothSame(_)) => "A",
            (SelfSigning::Ed25519Sha512, Index::Dual(_, _))
            | (SelfSigning::Ed25519Sha512, Index::BigDual(_, _)) => "2A",
            (SelfSigning::Ed25519Sha512, Index::CurrentOnly(_)) => "B",
            (SelfSigning::Ed25519Sha512, Index::BigCurrentOnly(_)) => "2B",
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BothSame(_)) => "C",
            (SelfSigning::ECDSAsecp256k1Sha256, Index::Dual(_, _)) => "C",
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BigDual(_, _)) => "2C",
            (SelfSigning::ECDSAsecp256k1Sha256, Index::CurrentOnly(_)) => "D",
            (SelfSigning::ECDSAsecp256k1Sha256, Index::BigCurrentOnly(_)) => "2D",
            (SelfSigning::Ed448, Index::BothSame(_)) => "0A",
            (SelfSigning::Ed448, Index::Dual(_, _)) => "0A",
            (SelfSigning::Ed448, Index::BigDual(_, _)) => "3A",
            (SelfSigning::Ed448, Index::CurrentOnly(_)) => "0B",
            (SelfSigning::Ed448, Index::BigCurrentOnly(_)) => "3B",
            (SelfSigning::ECDSA256r1Sha256, Index::BothSame(_)) => "Q",
            (SelfSigning::ECDSA256r1Sha256, Index::Dual(_, _)) => "Q",
            (SelfSigning::ECDSA256r1Sha256, Index::BigDual(_, _)) => "2Q",
            (SelfSigning::ECDSA256r1Sha256, Index::CurrentOnly(_)) => "R",
            (SelfSigning::ECDSA256r1Sha256, Index::BigCurrentOnly(_)) => "2R",
        };
        let indexes_str = match self.index {
            Index::BothSame(i) | Index::CurrentOnly(i) | Index::BigCurrentOnly(i) => {
                adjust_with_num(i, self.soft_size())
            }
            Index::Dual(i, pi) | Index::BigDual(i, pi) => [
                adjust_with_num(i, self.soft_size() / 2),
                adjust_with_num(pi, self.soft_size() / 2),
            ]
            .join(""),
        };
        [code, &indexes_str].join("")
    }
}

impl FromStr for AttachedSignatureCode {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match &s[..1] {
            "A" => Ok(Self::new(
                SelfSigning::Ed25519Sha512,
                Index::BothSame(b64_to_num(&s[1..2])?),
            )),
            "B" => Ok(Self::new(
                SelfSigning::Ed25519Sha512,
                Index::CurrentOnly(b64_to_num(&s[1..2])?),
            )),
            "C" => Ok(Self::new(
                SelfSigning::ECDSAsecp256k1Sha256,
                Index::BothSame(b64_to_num(&s[1..2])?),
            )),
            "D" => Ok(Self::new(
                SelfSigning::ECDSAsecp256k1Sha256,
                Index::CurrentOnly(b64_to_num(&s[1..2])?),
            )),
            "Q" => Ok(Self::new(
                SelfSigning::ECDSA256r1Sha256,
                Index::BothSame(b64_to_num(&s[1..2])?),
            )),
            "R" => Ok(Self::new(
                SelfSigning::ECDSA256r1Sha256,
                Index::CurrentOnly(b64_to_num(&s[1..2])?),
            )),
            "0" => match &s[1..2] {
                "A" => Ok(Self::new(
                    SelfSigning::Ed448,
                    Index::Dual(b64_to_num(&s[2..3])?, b64_to_num(&s[3..4])?),
                )),
                "B" => Ok(Self::new(
                    SelfSigning::Ed448,
                    Index::CurrentOnly(b64_to_num(&s[2..4])?),
                )),
                _ => Err(Error::UnknownCodeError),
            },
            "2" => match &s[1..2] {
                "A" => Ok(Self::new(
                    SelfSigning::Ed25519Sha512,
                    Index::BigDual(b64_to_num(&s[2..4])?, b64_to_num(&s[4..6])?),
                )),
                "B" => {
                    if b64_to_num(&s[4..6])? == 0 {
                        Ok(Self::new(
                            SelfSigning::Ed25519Sha512,
                            Index::BigCurrentOnly(b64_to_num(&s[2..4])?),
                        ))
                    } else {
                        Err(Error::EmptyCodeError)
                    }
                }
                "C" => Ok(Self::new(
                    SelfSigning::ECDSAsecp256k1Sha256,
                    Index::BigDual(b64_to_num(&s[2..4])?, b64_to_num(&s[4..6])?),
                )),
                "D" => Ok(Self::new(
                    SelfSigning::ECDSAsecp256k1Sha256,
                    Index::BigCurrentOnly(b64_to_num(&s[2..6])?),
                )),
                "Q" => Ok(Self::new(
                    SelfSigning::ECDSA256r1Sha256,
                    Index::BigDual(b64_to_num(&s[2..4])?, b64_to_num(&s[4..6])?),
                )),
                "R" => Ok(Self::new(
                    SelfSigning::ECDSA256r1Sha256,
                    Index::BigCurrentOnly(b64_to_num(&s[2..6])?),
                )),
                _ => Err(Error::UnknownCodeError),
            },
            "3" => match &s[1..2] {
                "A" => Ok(Self::new(
                    SelfSigning::Ed448,
                    Index::BigDual(b64_to_num(&s[2..5])?, b64_to_num(&s[5..8])?),
                )),
                "B" => Ok(Self::new(
                    SelfSigning::Ed448,
                    Index::BigCurrentOnly(b64_to_num(&s[2..8])?),
                )),
                _ => Err(Error::UnknownCodeError),
            },
            _ => Err(Error::UnknownCodeError),
        }
    }
}

#[test]
pub fn test() {
    let code = "2AADAC";
    let c: AttachedSignatureCode = code.parse().unwrap();
    assert_eq!(code, c.to_str());

    let code = "2AAAAB";
    let c: AttachedSignatureCode = code.parse().unwrap();
    assert_eq!(code, c.to_str());
}

#[test]
pub fn test_ecdsa_256r1_indexed_sig_roundtrip() {
    for code in [
        "QA",
        "RB",
        "2QADAC",
        "2RAAAD",
    ] {
        let c: AttachedSignatureCode = code.parse().unwrap();
        assert_eq!(code, c.to_str(), "round-trip failed for {code}");
        assert_eq!(c.code, SelfSigning::ECDSA256r1Sha256);
    }
}