1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use base64::{decode_config, encode_config, DecodeError, URL_SAFE};
use failure::Fail;
use openssl::{
    bn::BigNum,
    error::ErrorStack,
    pkey::{HasPublic, Public},
    rsa::Rsa,
};

#[derive(Clone, Debug, Fail)]
pub enum KeyError {
    #[fail(display = "Error creating public key: {}", _0)]
    OpenSSL(#[cause] ErrorStack),

    #[fail(display = "Error decoding base64: {}", _0)]
    Base64(#[cause] DecodeError),

    #[fail(display = "Magic key isn't RSA")]
    Rsa,

    #[fail(display = "Magic key is malformed")]
    Malformed,
}

impl From<ErrorStack> for KeyError {
    fn from(e: ErrorStack) -> Self {
        KeyError::OpenSSL(e)
    }
}

impl From<DecodeError> for KeyError {
    fn from(e: DecodeError) -> Self {
        KeyError::Base64(e)
    }
}

pub trait AsMagicPublicKey {
    fn as_magic_public_key(&self) -> String;
}

pub trait FromMagicPublicKey: Sized {
    fn from_magic_public_key(magic_public_key: &str) -> Result<Self, KeyError>;
}

impl<T> AsMagicPublicKey for Rsa<T>
where
    T: HasPublic,
{
    fn as_magic_public_key(&self) -> String {
        let n = encode_config(&self.n().to_vec(), URL_SAFE);
        let e = encode_config(&self.e().to_vec(), URL_SAFE);

        format!("RSA.{}.{}", n, e)
    }
}

impl FromMagicPublicKey for Rsa<Public> {
    fn from_magic_public_key(magic_public_key: &str) -> Result<Self, KeyError> {
        let mut iter = magic_public_key.split('.');
        let kind = iter.next().ok_or(KeyError::Malformed)?;

        match kind {
            "RSA" => (),
            _ => return Err(KeyError::Rsa),
        };

        let n = iter.next().ok_or(KeyError::Malformed)?;
        let e = iter.next().ok_or(KeyError::Malformed)?;

        let n = decode_config(n, URL_SAFE)?;
        let e = decode_config(e, URL_SAFE)?;

        let n = BigNum::from_slice(&n)?;
        let e = BigNum::from_slice(&e)?;

        Ok(Rsa::from_public_components(n, e)?)
    }
}

#[cfg(test)]
mod tests {
    use crate::{AsMagicPublicKey, FromMagicPublicKey};
    use openssl::rsa::Rsa;

    #[test]
    fn can_complete_cycle() {
        let rsa = Rsa::generate(2048).unwrap();

        let string = rsa.as_magic_public_key();

        let res = Rsa::from_magic_public_key(&string);

        assert!(res.is_ok());
    }

    #[test]
    fn asonix_key_is_valid() {
        key_is_valid("RSA.wEvEsHqM3twoC2F3KYMQ9YOialfVQX4StkLvhLUwFv8qpmY7ZSHHl2TWpnzlo5iWS5Pi2vC41HUGYz9XT5G74IUOyuIkjTL1FIcPJDcUFCzQjN3QZcHLPJPJVNOOOEiOk8__paOyrqJTq9ikcJDMJ8KTWQgk1leOxUVEN5uaQ-p9IBFbXC76-RqabfEoqLZagVMDSOfeC2uR9xZ1q5HkFveRTGs84QLR7FJVvx078nszx4UQGnmP0M-0sOeRJGK17IoJmhaok1XBpP6XFQ45vYeIRiaFj0Pc9GNISCW70dVXKMhv-K07orQJm6PwP8USyhq4tLkq6tcPbGRqEk3ZXw==.AQAB");
    }

    #[test]
    fn sir_boops_key_is_valid() {
        key_is_valid("RSA.vwDujxmxoYHs64MyVB3LG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ-3Zb6CI8zOO-nM-Q2llrVRYjZa4ZFnOLvMTq_Kf-Zf5wy2aCRer88gX-MsJOAtItSi412y0a_rKOuFaDYLOLeTkRvmGLgZWbsrZJOp-YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBOfOHggSt1-eAIKGIsCmINEMzs1mG9D75xKtC_sM8GfbvBclQcBstGkHAEj1VHPW0ch6Bok5_QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Qaw==.AQAB");
    }

    fn key_is_valid(key: &str) {
        let res = Rsa::from_magic_public_key(key);

        assert!(res.is_ok());

        assert_eq!(res.unwrap().as_magic_public_key(), key);
    }
}