use alloc::{string::String, vec, vec::Vec};
use core::{cmp, fmt, hash, str::FromStr};
use super::multihash;
use crate::util::protobuf;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PublicKey {
Ed25519([u8; 32]),
}
impl PublicKey {
pub fn to_protobuf_encoding(&self) -> Vec<u8> {
match self {
PublicKey::Ed25519(key) => {
const CAPACITY: usize = 32 + 4;
let mut out = Vec::with_capacity(CAPACITY);
for slice in protobuf::enum_tag_encode(1, 1) {
out.extend_from_slice(slice.as_ref());
}
for slice in protobuf::bytes_tag_encode(2, key) {
out.extend_from_slice(slice.as_ref());
}
debug_assert_eq!(out.len(), CAPACITY);
out
}
}
}
pub fn from_protobuf_encoding(bytes: &[u8]) -> Result<PublicKey, FromProtobufEncodingError> {
struct ErrorWrapper(FromProtobufEncodingError);
impl<'a> nom::error::ParseError<&'a [u8]> for ErrorWrapper {
fn from_error_kind(_: &'a [u8], _: nom::error::ErrorKind) -> Self {
ErrorWrapper(FromProtobufEncodingError::ProtobufDecodeError)
}
fn append(_: &'a [u8], _: nom::error::ErrorKind, other: Self) -> Self {
other
}
}
impl<'a> nom::error::FromExternalError<&'a [u8], FromProtobufEncodingError> for ErrorWrapper {
fn from_external_error(
_: &'a [u8],
_: nom::error::ErrorKind,
e: FromProtobufEncodingError,
) -> Self {
ErrorWrapper(e)
}
}
let mut parser =
nom::combinator::all_consuming::<_, ErrorWrapper, _>(nom::combinator::complete((
nom::sequence::preceded(
nom::combinator::peek(nom::combinator::verify(
protobuf::tag_decode,
|(field_num, _)| *field_num == 1,
)),
nom::combinator::map_res(protobuf::enum_tag_decode, |val| match val {
0..=3 => Ok(val),
_ => Err(FromProtobufEncodingError::UnknownAlgorithm),
}),
),
nom::sequence::preceded(
nom::combinator::peek(nom::combinator::verify(
protobuf::tag_decode,
|(field_num, _)| *field_num == 2,
)),
nom::combinator::map_res(protobuf::bytes_tag_decode, |d| {
<[u8; 32]>::try_from(d)
.map_err(|_| FromProtobufEncodingError::BadEd25519Key)
}),
),
)));
match nom::Finish::finish(nom::Parser::parse(&mut parser, bytes)) {
Ok((_, (1, key))) => Ok(PublicKey::Ed25519(key)),
Ok((_, (_, _))) => Err(FromProtobufEncodingError::UnsupportedAlgorithm),
Err(err) => Err(err.0),
}
}
pub fn into_peer_id(self) -> PeerId {
self.into()
}
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), SignatureVerifyFailed> {
let PublicKey::Ed25519(public_key) = self;
let public_key = ed25519_zebra::VerificationKey::try_from(*public_key)
.map_err(|_| SignatureVerifyFailed())?;
let signature =
ed25519_zebra::Signature::try_from(signature).map_err(|_| SignatureVerifyFailed())?;
public_key
.verify(&signature, message)
.map_err(|_| SignatureVerifyFailed())?;
Ok(())
}
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum FromProtobufEncodingError {
ProtobufDecodeError,
UnknownAlgorithm,
BadEd25519Key,
UnsupportedAlgorithm,
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub struct SignatureVerifyFailed();
const MAX_INLINE_KEY_LENGTH: usize = 42;
#[derive(Clone, Eq)]
pub struct PeerId {
multihash: Vec<u8>,
}
impl PeerId {
pub fn from_public_key(key: &PublicKey) -> PeerId {
let key_enc = key.to_protobuf_encoding();
let out = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
multihash::Multihash::identity(&key_enc).into_bytes()
} else {
let mut out = vec![0; 34];
out[0] = 0x12;
out[1] = 0x32;
let mut hasher = <sha2::Sha256 as sha2::Digest>::new();
sha2::Digest::update(&mut hasher, &key_enc);
sha2::Digest::finalize_into(
hasher,
sha2::digest::generic_array::GenericArray::from_mut_slice(&mut out[2..]),
);
out
};
PeerId { multihash: out }
}
pub fn from_bytes(data: Vec<u8>) -> Result<PeerId, (FromBytesError, Vec<u8>)> {
let result = match multihash::Multihash::from_bytes(&data) {
Ok(hash) => {
if hash.hash_algorithm_code() == 0 {
if let Err(err) = PublicKey::from_protobuf_encoding(hash.data()) {
Err(FromBytesError::InvalidPublicKey(err))
} else {
Ok(())
}
} else if hash.hash_algorithm_code() == 0x12 {
Ok(())
} else {
Err(FromBytesError::InvalidMultihashAlgorithm)
}
}
Err((err, _)) => Err(FromBytesError::DecodeError(err)),
};
match result {
Ok(()) => Ok(PeerId { multihash: data }),
Err(err) => Err((err, data)),
}
}
pub fn into_bytes(self) -> Vec<u8> {
self.multihash
}
pub fn as_bytes(&self) -> &[u8] {
&self.multihash
}
pub fn to_base58(&self) -> String {
bs58::encode(self.as_bytes()).into_string()
}
}
impl<'a> From<&'a PublicKey> for PeerId {
fn from(public_key: &'a PublicKey) -> PeerId {
PeerId::from_public_key(public_key)
}
}
impl From<PublicKey> for PeerId {
fn from(public_key: PublicKey) -> PeerId {
PeerId::from_public_key(&public_key)
}
}
impl fmt::Debug for PeerId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for PeerId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_base58().fmt(f)
}
}
impl cmp::PartialOrd for PeerId {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(Ord::cmp(self, other))
}
}
impl cmp::Ord for PeerId {
fn cmp(&self, other: &Self) -> cmp::Ordering {
let lhs: &[u8] = self.as_ref();
let rhs: &[u8] = other.as_ref();
lhs.cmp(rhs)
}
}
impl hash::Hash for PeerId {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
let digest = self.as_ref() as &[u8];
hash::Hash::hash(digest, state);
}
}
impl TryFrom<Vec<u8>> for PeerId {
type Error = FromBytesError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
PeerId::from_bytes(value).map_err(|(err, _)| err)
}
}
impl PartialEq<PeerId> for PeerId {
fn eq(&self, other: &PeerId) -> bool {
let self_digest = self.as_ref() as &[u8];
let other_digest = other.as_ref() as &[u8];
self_digest == other_digest
}
}
impl AsRef<[u8]> for PeerId {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl FromStr for PeerId {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bytes = bs58::decode(s)
.into_vec()
.map_err(Bs58DecodeError)
.map_err(ParseError::Bs58)?;
PeerId::from_bytes(bytes).map_err(|(err, _)| ParseError::NotPeerId(err))
}
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum FromBytesError {
DecodeError(multihash::FromBytesError),
InvalidMultihashAlgorithm,
#[display("Failed to decode public key protobuf: {_0}")]
InvalidPublicKey(FromProtobufEncodingError),
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum ParseError {
#[display("Base58 decoding error: {_0}")]
Bs58(Bs58DecodeError),
#[display("{_0}")]
NotPeerId(FromBytesError),
}
#[derive(Debug, derive_more::Display, derive_more::Error, derive_more::From)]
#[display("{_0}")]
pub struct Bs58DecodeError(#[error(not(source))] bs58::decode::Error);
#[cfg(test)]
mod tests {
#[test]
fn encode_decode_pubkey() {
let pub_key = super::PublicKey::Ed25519(rand::random());
let protobuf = pub_key.to_protobuf_encoding();
assert_eq!(
super::PublicKey::from_protobuf_encoding(&protobuf).unwrap(),
pub_key
);
}
}