extern crate hex;
use rust_sodium::crypto::hash::sha512;
use std::net::Ipv6Addr;
pub use cryptography::crypto_box;
pub const PUBLIC_KEY_BYTES: usize = crypto_box::PUBLICKEYBYTES;
pub const SECRET_KEY_BYTES: usize = crypto_box::SECRETKEYBYTES;
pub trait FromBase32: Sized {
fn from_base32(characters: &[u8]) -> Option<Self>;
}
pub trait ToBase32: Sized {
fn to_base32(&self) -> String;
}
pub trait FromHex: Sized {
fn from_hex(characters: &[u8]) -> Option<Self>;
}
pub trait ToHex: Sized {
fn to_hex(&self) -> String;
}
const BASE32_DECODING_TABLE: [u8; 128] = [
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 99, 99, 99, 99, 99, 99, 99, 99, 10, 11, 12, 99, 13, 14, 15, 99,
16, 17, 18, 19, 20, 99, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 99, 99, 99, 99, 99, 99, 99,
10, 11, 12, 99, 13, 14, 15, 99, 16, 17, 18, 19, 20, 99, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 99, 99, 99, 99, 99,
];
pub fn decode_base32(encoded: &[u8]) -> Result<Vec<u8>, u8> {
let mut res = Vec::with_capacity(encoded.len() * 5 / 8);
let mut last_word_length = 0;
let mut last_word = 0u16;
for c in encoded.iter() {
let c = c.clone();
if c >= 128 {
return Err(c);
}
let bits = BASE32_DECODING_TABLE[c as usize];
if bits == 99 {
return Err(c);
}
last_word += (bits as u16) << last_word_length;
last_word_length += 5;
if last_word_length >= 8 {
last_word_length -= 8;
res.push((last_word & 0b11111111) as u8);
last_word >>= 8;
}
}
if last_word > 0 {
res.push(last_word as u8);
}
Ok(res)
}
#[test]
fn test_decode_base32() {
assert_eq!(decode_base32(b"4321"), Ok(vec![0x64, 0x88]));
decode_base32(b"2j1xz5k5y1xwz7kcczc4565jurhp8bbz1lqfu9kljw36p3nmb050").unwrap();
}
impl FromBase32 for crypto_box::PublicKey {
fn from_base32(characters: &[u8]) -> Option<crypto_box::PublicKey> {
assert!(characters.len() >= 2);
let length: usize = characters.len() - 2;
assert_eq!(characters[length..], b".k".to_owned());
if let Ok(bytes) = decode_base32(&characters[..length]) {
crypto_box::PublicKey::from_slice(&bytes)
} else {
None
}
}
}
const BASE32_ENCODING_TABLE: [u8; 32] = [
'0' as u8, '1' as u8, '2' as u8, '3' as u8, '4' as u8, '5' as u8, '6' as u8, '7' as u8,
'8' as u8, '9' as u8, 'b' as u8, 'c' as u8, 'd' as u8, 'f' as u8, 'g' as u8, 'h' as u8,
'j' as u8, 'k' as u8, 'l' as u8, 'm' as u8, 'n' as u8, 'p' as u8, 'q' as u8, 'r' as u8,
's' as u8, 't' as u8, 'u' as u8, 'v' as u8, 'w' as u8, 'x' as u8, 'y' as u8, 'z' as u8,
];
pub fn encode_base32(bytes: &[u8]) -> Vec<u8> {
let mut encoded: Vec<u8> = Vec::new();
encoded.reserve(bytes.len() * 8 / 5);
let mut window = 0u16;
let mut bits_in_window = 0;
let mut bytes_offset = 0;
assert!(BASE32_ENCODING_TABLE.len() == 32);
while bytes_offset < bytes.len() || window > 0 {
if bits_in_window < 5 {
let byte = *bytes.get(bytes_offset).unwrap_or(&0);
bytes_offset += 1;
window += (byte as u16) << bits_in_window;
bits_in_window += 8;
}
let char_index = (window & 0b11111) as usize;
window >>= 5;
bits_in_window -= 5;
encoded.push(BASE32_ENCODING_TABLE[char_index]);
}
encoded
}
#[test]
fn test_encode_decode_base32() {
let key = b"2j1xz5k5y1xwz7kcczc4565jurhp8bbz1lqfu9kljw36p3nmb050";
let expected = "2j1xz5k5y1xwz7kcczc4565jurhp8bbz1lqfu9kljw36p3nmb05";
assert_eq!(
String::from_utf8(encode_base32(&decode_base32(key).unwrap())).unwrap(),
expected
)
}
#[test]
fn test_encode_base32() {
assert_eq!(
String::from_utf8(encode_base32(&vec![0x64, 0x88])).unwrap(),
"4321"
);
}
impl ToBase32 for crypto_box::PublicKey {
fn to_base32(&self) -> String {
let mut repr = encode_base32(&self.0);
assert!(repr.len() <= 52);
repr.resize(52, '0' as u8);
repr.push('.' as u8);
repr.push('k' as u8);
String::from_utf8(repr).unwrap()
}
}
pub fn publickey_to_ipv6addr(pk: &crypto_box::PublicKey) -> Ipv6Addr {
let digest1 = sha512::hash(&pk.0);
let digest2 = sha512::hash(&digest1.0);
let mut octets = [0u8; 16];
octets.copy_from_slice(&digest2.0[0..16]);
Ipv6Addr::from(octets)
}
impl FromHex for crypto_box::SecretKey {
fn from_hex(characters: &[u8]) -> Option<crypto_box::SecretKey> {
let res = hex::decode(characters);
match res {
Ok(vec) => {
if vec.len() == 32 {
let mut bytes = [0u8; 32];
bytes.copy_from_slice(&vec);
crypto_box::SecretKey::from_slice(&bytes)
} else {
None
}
}
Err(_) => None,
}
}
}
impl ToHex for crypto_box::SecretKey {
fn to_hex(&self) -> String {
hex::encode(self.0)
}
}
#[test]
fn test_hex() {
assert_eq!(hex::encode(vec![0, 0]), "0000");
}