use crate::{PUBLIC_KEY_LEN, ParsePublicKeyError, PublicKeyBytes, PublicKeyEncoding};
pub fn decode(
encoding: PublicKeyEncoding,
input: impl AsRef<str>,
) -> Result<PublicKeyBytes, ParsePublicKeyError> {
use ParsePublicKeyError::*;
use PublicKeyEncoding::*;
let input = input.as_ref();
let mut buffer = [0u8; PUBLIC_KEY_LEN];
Ok(match encoding {
Base16 => input.parse::<PublicKeyBytes>()?,
#[cfg(feature = "base58")]
Asimov => {
let Some(input) = input.strip_prefix("ⒶY") else {
return Err(InvalidPrefix);
};
let len = bs58::decode(input).onto(&mut buffer)?;
PublicKeyBytes::from_slice(&buffer[..len])?
},
#[cfg(feature = "base58")]
Base58 => {
let len = bs58::decode(input).onto(&mut buffer)?;
PublicKeyBytes::from_slice(&buffer[..len])?
},
#[cfg(feature = "base64")]
Base64 => {
let mut buffer = [0u8; PUBLIC_KEY_LEN + 1];
let len = decode_data_encoding(&data_encoding::BASE64, input.as_bytes(), &mut buffer)?;
PublicKeyBytes::from_slice(&buffer[..len])?
},
#[cfg(feature = "base64")]
Base64Url => {
let len = decode_data_encoding(
&data_encoding::BASE64URL_NOPAD,
input.as_bytes(),
&mut buffer,
)?;
PublicKeyBytes::from_slice(&buffer[..len])?
},
#[cfg(feature = "multibase")]
Multibase => {
let Some(input) = input.strip_prefix("z") else {
return Err(InvalidPrefix);
};
let mut buffer = [0u8; 2 + PUBLIC_KEY_LEN];
let len = bs58::decode(input).onto(&mut buffer)?;
if len != buffer.len() {
return Err(InvalidLength(len));
}
if buffer[0] != 0xed {
return Err(InvalidPrefix);
}
if buffer[1] != 0x01 {
return Err(InvalidPrefix);
}
PublicKeyBytes::from_slice(&buffer[2..])?
},
#[cfg(feature = "base58")]
Near => {
let Some(input) = input.strip_prefix("ed25519:") else {
return Err(InvalidPrefix);
};
let len = bs58::decode(input).onto(&mut buffer)?;
PublicKeyBytes::from_slice(&buffer[..len])?
},
#[cfg(feature = "base64")]
OpenSsh => {
let input = input
.strip_prefix("ssh-ed25519 ")
.unwrap_or(input)
.as_bytes();
const BUFFER_LEN: usize =
size_of::<u32>() + b"ssh-ed25519".len() + size_of::<u32>() + PUBLIC_KEY_LEN;
let mut buffer = [0u8; BUFFER_LEN];
let len = decode_data_encoding(&data_encoding::BASE64, input, &mut buffer)?;
if len != buffer.len() {
return Err(InvalidLength(len));
}
let input = buffer;
let (slice, input) = input.split_at(size_of::<u32>());
if !slice.eq(&(b"ssh-ed25519".len() as u32).to_be_bytes()) {
return Err(InvalidChars);
};
let (slice, input) = input.split_at(b"ssh-ed25519".len());
if !slice.eq(b"ssh-ed25519") {
return Err(InvalidChars);
};
let (slice, input) = input.split_at(size_of::<u32>());
if !slice.eq(&(PUBLIC_KEY_LEN as u32).to_be_bytes()) {
return Err(InvalidChars);
};
PublicKeyBytes::from_slice(input)?
},
#[cfg(feature = "base32z")]
Base32z => {
let encoding = data_encoding_macro::new_encoding! {
symbols: "ybndrfg8ejkmcpqxot1uwisza345h769",
};
let len = decode_data_encoding(&encoding, input.as_bytes(), &mut buffer)?;
PublicKeyBytes::from_slice(&buffer[..len])?
},
})
}
#[cfg(any(feature = "base32z", feature = "base64"))]
fn decode_data_encoding(
encoding: &data_encoding::Encoding,
input: &[u8],
buffer: &mut [u8],
) -> Result<usize, ParsePublicKeyError> {
let decode_len = encoding.decode_len(input.len())?;
if decode_len > buffer.len() {
return Err(ParsePublicKeyError::InvalidLength(input.len()));
}
Ok(encoding.decode_mut(input, &mut buffer[..decode_len])?)
}