use base85::{encode, decode};
use regex::Regex;
lazy_static! {
static ref RE_CRYPTOSTRING_FORMAT: regex::Regex = {
Regex::new(r"^([A-Z0-9-]{1,24}):([0-9A-Za-z!#$%&()*+-;<=>?@^_`{|}~]+)$").unwrap()
};
static ref RE_CRYPTOSTRING_PREFIX: regex::Regex = {
Regex::new(r"^([A-Z0-9-]{1,24})$").unwrap()
};
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct CryptoString {
string: String
}
impl ToString for CryptoString {
fn to_string(&self) -> String {
self.string.clone()
}
}
impl CryptoString {
pub fn from(s: &str) -> Option<CryptoString> {
let caps = RE_CRYPTOSTRING_FORMAT.captures(s);
match caps {
Some(_) => {
Some(CryptoString {string: String::from(s)})
},
_ => None
}
}
pub fn from_bytes(algorithm: &str, buffer: &[u8]) -> Option<CryptoString> {
if !RE_CRYPTOSTRING_PREFIX.is_match(algorithm) {
return None
}
let mut out = CryptoString {string: String::from(algorithm)+":"};
let encstr = encode(buffer);
if encstr.len() == 0 {
return None
}
out.string.push_str(&encstr);
Some(out)
}
pub fn as_bytes(&self) -> &[u8] {
self.string.as_bytes()
}
pub fn as_raw(&self) -> Vec<u8> {
let list: Vec<&str> = self.string.split(":").collect();
return decode(list[1]).unwrap()
}
pub fn as_str(&self) -> &str {
self.string.as_str()
}
pub fn is_empty(&self) -> bool {
self.string.is_empty()
}
pub fn prefix(&self) -> &str {
let list: Vec<&str> = self.string.split(":").collect();
list[0]
}
pub fn data(&self) -> &str {
let list: Vec<&str> = self.string.split(":").collect();
list[1]
}
}
#[cfg(test)]
mod tests {
#[test]
fn cs_test_from() {
let testlist = [
"ED25519:r#r*RiXIN-0n)BzP3bv`LA&t4LFEQNF0Q@$N~RF*",
"BLAKE2B-256:-Zz4O7J;m#-rB)2llQ*xTHjtblwm&kruUVa_v(&W",
"CURVE25519:SNhj2K`hgBd8>G>lW$!pXiM7S-B!Fbd9jT2&{{Az"
];
for test in testlist.iter() {
match crate::CryptoString::from(test) {
None => panic!("from() test failure"),
_ => { },
}
}
let faillist = [
"ED25519",
":123456789",
"$ILLEGAL:123456789",
" ",
""
];
for test in faillist.iter() {
match crate::CryptoString::from(test) {
None => { },
_ => panic!("from() bad value test failure"),
}
}
}
#[test]
fn cs_test_from_bytes_plus() {
let faillist = [
("", ":123456789"),
("$ILLEGAL", "123456789"),
("TEST", ""),
];
for test in faillist.iter() {
match crate::CryptoString::from_bytes(test.0, test.1.as_bytes()) {
None => { },
_ => panic!("from_bytes() bad value test failure"),
}
}
let cs = match crate::CryptoString::from_bytes("TEST", b"aaaaaa") {
Some(v) => v,
_ => panic!("from_bytes() test failure"),
};
assert_eq!(cs.prefix(), "TEST");
assert_eq!(cs.as_str(), "TEST:VPRomVPO");
assert_eq!(cs.as_bytes(), b"TEST:VPRomVPO");
assert_eq!(cs.as_raw(), b"aaaaaa");
assert_eq!(cs.is_empty(), false);
}
}