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); } }