cncs-sm2-kit 0.1.3

Chinese National Cryptography Standard - SM2 Kit
Documentation
use core::fmt;

use num_bigint::{BigUint, ParseBigIntError};
use num_traits::Num as _;

#[derive(Clone)]
pub struct PublicKey {
    pub x: BigUint,
    pub y: BigUint,
}
impl fmt::Debug for PublicKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("PublicKey")
            .field("x", &self.x.to_str_radix(16).to_uppercase())
            .field("y", &self.y.to_str_radix(16).to_uppercase())
            .finish()
    }
}
impl fmt::Display for PublicKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", &self.to_concated_hex_str())
    }
}

impl PublicKey {
    pub fn new(x: BigUint, y: BigUint) -> Self {
        Self { x, y }
    }

    pub fn from_bytes(x_bytes: &[u8], y_bytes: &[u8]) -> Self {
        let x = BigUint::from_bytes_be(x_bytes);
        let y = BigUint::from_bytes_be(y_bytes);
        Self { x, y }
    }

    pub fn from_hex_str(x_hex_str: &str, y_hex_str: &str) -> Result<Self, ParseBigIntError> {
        let x = BigUint::from_str_radix(x_hex_str, 16)?;
        let y = BigUint::from_str_radix(y_hex_str, 16)?;
        Ok(Self { x, y })
    }

    pub fn from_concated_hex_str(hex_str: &str) -> Result<Self, PublicKeyFromConcatedHexStrError> {
        match hex_str.len() {
            130 => {
                if !hex_str.starts_with("04") {
                    return Err(PublicKeyFromConcatedHexStrError::Invalid);
                }
                Self::from_hex_str(&hex_str[2..66], &hex_str[66..])
                    .map_err(PublicKeyFromConcatedHexStrError::ParseBigIntError)
            }
            128 => Self::from_hex_str(&hex_str[..64], &hex_str[64..])
                .map_err(PublicKeyFromConcatedHexStrError::ParseBigIntError),
            _ => Err(PublicKeyFromConcatedHexStrError::Invalid),
        }
    }

    pub fn to_concated_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(64);
        bytes.extend(super::to_bytes::<32>(&self.x));
        bytes.extend(super::to_bytes::<32>(&self.y));
        bytes
    }

    pub fn to_concated_hex_str(&self) -> String {
        super::to_hex_str(&self.to_concated_bytes())
    }
}

#[derive(Debug)]
pub enum PublicKeyFromConcatedHexStrError {
    Invalid,
    ParseBigIntError(ParseBigIntError),
}
impl fmt::Display for PublicKeyFromConcatedHexStrError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
impl std::error::Error for PublicKeyFromConcatedHexStrError {}

//
//
//
impl TryFrom<&PublicKey> for libsm::sm2::ecc::Point {
    type Error = String;

    fn try_from(k: &PublicKey) -> Result<Self, Self::Error> {
        let curve = libsm::sm2::ecc::EccCtx::new();

        curve
            .new_point(
                &libsm::sm2::field::FieldElem::from_biguint(&k.x),
                &libsm::sm2::field::FieldElem::from_biguint(&k.y),
            )
            .map_err(|err| err.to_string())
    }
}

impl From<&PublicKey> for gmsm::g2::subject::PublicKey {
    fn from(k: &PublicKey) -> Self {
        Self {
            x: k.x.to_owned(),
            y: k.y.to_owned(),
        }
    }
}

impl From<&gmsm::g2::subject::PublicKey> for PublicKey {
    fn from(k: &gmsm::g2::subject::PublicKey) -> Self {
        Self::new(k.x.to_owned(), k.y.to_owned())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::{PUBLIC_KEY_X, PUBLIC_KEY_Y};

    #[test]
    fn test_from_concated_hex_str() {
        let public_key =
            PublicKey::from_concated_hex_str(format!("{}{}", PUBLIC_KEY_X, PUBLIC_KEY_Y).as_str())
                .unwrap();
        assert_eq!(
            public_key.x,
            BigUint::from_str_radix(PUBLIC_KEY_X, 16).unwrap()
        );
        assert_eq!(
            public_key.y,
            BigUint::from_str_radix(PUBLIC_KEY_Y, 16).unwrap()
        );

        //
        let public_key = PublicKey::from_concated_hex_str(
            format!("04{}{}", PUBLIC_KEY_X, PUBLIC_KEY_Y).as_str(),
        )
        .unwrap();
        assert_eq!(
            public_key.x,
            BigUint::from_str_radix(PUBLIC_KEY_X, 16).unwrap()
        );
        assert_eq!(
            public_key.y,
            BigUint::from_str_radix(PUBLIC_KEY_Y, 16).unwrap()
        );
    }

    #[test]
    fn test_get_point_for_verify() {
        let public_key = PublicKey::from_hex_str(PUBLIC_KEY_X, PUBLIC_KEY_Y).unwrap();
        for _ in 1..=3 {
            let libsm::sm2::ecc::Point { x, y, z } =
                libsm::sm2::ecc::Point::try_from(&public_key).unwrap();
            println!(
                "{} {} {}",
                x.to_biguint().to_str_radix(16).to_uppercase(),
                y.to_biguint().to_str_radix(16).to_uppercase(),
                z.to_biguint().to_str_radix(16).to_uppercase()
            );
            assert_eq!(
                x.to_biguint(),
                BigUint::from_str_radix(PUBLIC_KEY_X, 16).unwrap()
            );
            assert_eq!(
                y.to_biguint(),
                BigUint::from_str_radix(PUBLIC_KEY_Y, 16).unwrap()
            );
            assert_eq!(z.to_biguint(), BigUint::from_str_radix("1", 16).unwrap());
        }
    }

    #[test]
    fn test_to_concated_hex_str() {
        let public_key = PublicKey::from_hex_str(PUBLIC_KEY_X, PUBLIC_KEY_Y).unwrap();
        println!("{:?}", public_key);
        println!("{}", public_key);
        assert_eq!(
            public_key.to_concated_hex_str(),
            format!("{}{}", PUBLIC_KEY_X, PUBLIC_KEY_Y)
        );

        const PUBLIC_KEY: &str = "0F9E448FEBF2C412AB30127BAC3ADDA97B4206274E668D6253C03E889EE73A56A4304567EE2ACFECEEB8BB7DC7ED0E928A2449BFA0606F0984B6CE704DBB81DA";
        let public_key = PublicKey::from_concated_hex_str(PUBLIC_KEY).unwrap();
        assert_eq!(public_key.to_concated_hex_str(), PUBLIC_KEY);
    }
}