use std::fmt::{
self,
Debug,
Display,
Formatter,
};
use std::hash::{
Hash,
Hasher,
};
use std::str::FromStr;
use ed25519_dalek::ed25519::signature::DigestVerifier;
use ed25519_dalek::Verifier;
use hedera_proto::services;
use k256::ecdsa;
use pkcs8::der::{
Decode,
Encode,
};
use prost::Message;
use sha2::Digest;
use crate::key::private_key::ED25519_OID;
use crate::protobuf::ToProtobuf;
use crate::{
AccountId,
Error,
FromProtobuf,
};
#[cfg(test)]
mod tests;
#[derive(Clone, Eq, Copy, Hash, PartialEq)]
#[cfg_attr(feature = "ffi", derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]
pub struct PublicKey(PublicKeyData);
#[derive(Clone, Copy)]
enum PublicKeyData {
Ed25519(ed25519_dalek::PublicKey),
Ecdsa(k256::ecdsa::VerifyingKey),
}
impl Hash for PublicKeyData {
fn hash<H: Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match &self {
PublicKeyData::Ed25519(key) => key.to_bytes().hash(state),
PublicKeyData::Ecdsa(key) => key.to_bytes().hash(state),
}
}
}
impl PartialEq for PublicKeyData {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Ed25519(l0), Self::Ed25519(r0)) => l0 == r0,
(Self::Ecdsa(l0), Self::Ecdsa(r0)) => l0 == r0,
_ => false,
}
}
}
impl Eq for PublicKeyData {}
impl PublicKey {
pub(super) fn ed25519(key: ed25519_dalek::PublicKey) -> Self {
Self(PublicKeyData::Ed25519(key))
}
pub(super) fn ecdsa(key: k256::ecdsa::VerifyingKey) -> Self {
Self(PublicKeyData::Ecdsa(key))
}
#[must_use]
pub fn is_ed25519(&self) -> bool {
matches!(&self.0, PublicKeyData::Ed25519(_))
}
#[must_use]
pub fn is_ecdsa(&self) -> bool {
matches!(&self.0, PublicKeyData::Ecdsa(_))
}
pub(crate) fn from_alias_bytes(bytes: &[u8]) -> crate::Result<Option<Self>> {
if bytes.is_empty() {
return Ok(None);
}
Ok(Some(PublicKey::from_protobuf(
services::Key::decode(bytes).map_err(Error::from_protobuf)?,
)?))
}
pub fn from_bytes(bytes: &[u8]) -> crate::Result<Self> {
if bytes.len() == 32 {
return Self::from_bytes_ed25519(bytes);
}
if bytes.len() == 33 {
return Self::from_bytes_ecdsa(bytes);
}
Self::from_bytes_der(bytes)
}
pub fn from_bytes_ed25519(bytes: &[u8]) -> crate::Result<Self> {
let data = if bytes.len() == 32 {
ed25519_dalek::PublicKey::from_bytes(bytes).map_err(Error::key_parse)?
} else {
return Self::from_bytes_der(bytes);
};
Ok(Self::ed25519(data))
}
pub fn from_bytes_ecdsa(bytes: &[u8]) -> crate::Result<Self> {
let data = if bytes.len() == 33 {
k256::ecdsa::VerifyingKey::from_sec1_bytes(bytes).map_err(Error::key_parse)?
} else {
return Self::from_bytes_der(bytes);
};
Ok(Self::ecdsa(data))
}
pub fn from_bytes_der(bytes: &[u8]) -> crate::Result<Self> {
let info = pkcs8::SubjectPublicKeyInfo::from_der(bytes)
.map_err(|err| Error::key_parse(err.to_string()))?;
if info.algorithm.oid == k256::elliptic_curve::ALGORITHM_OID {
return Self::from_bytes_ecdsa(info.subject_public_key);
}
if info.algorithm.oid == ED25519_OID {
return Self::from_bytes_ed25519(info.subject_public_key);
}
Err(Error::key_parse(format!("unsupported key algorithm: {}", info.algorithm.oid)))
}
pub fn from_str_der(s: &str) -> crate::Result<Self> {
Self::from_bytes_der(
&hex::decode(s.strip_prefix("0x").unwrap_or(s)).map_err(Error::key_parse)?,
)
}
pub fn from_str_ed25519(s: &str) -> crate::Result<Self> {
Self::from_bytes_ed25519(
&hex::decode(s.strip_prefix("0x").unwrap_or(s)).map_err(Error::key_parse)?,
)
}
pub fn from_str_ecdsa(s: &str) -> crate::Result<Self> {
Self::from_bytes_ecdsa(
&hex::decode(s.strip_prefix("0x").unwrap_or(s)).map_err(Error::key_parse)?,
)
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
match &self.0 {
PublicKeyData::Ed25519(_) => self.to_bytes_raw(),
PublicKeyData::Ecdsa(_) => self.to_bytes_der(),
}
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn to_bytes_der(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(64);
match &self.0 {
PublicKeyData::Ed25519(key) => {
let key = key.to_bytes();
let info = pkcs8::SubjectPublicKeyInfo {
algorithm: self.algorithm(),
subject_public_key: &key,
};
info.encode_to_vec(&mut buf).unwrap();
}
PublicKeyData::Ecdsa(key) => {
let key = key.to_bytes();
let info = pkcs8::SubjectPublicKeyInfo {
algorithm: self.algorithm(),
subject_public_key: key.as_slice(),
};
info.encode_to_vec(&mut buf).unwrap();
}
}
buf
}
fn algorithm(&self) -> pkcs8::AlgorithmIdentifier<'_> {
pkcs8::AlgorithmIdentifier {
parameters: None,
oid: match self.0 {
PublicKeyData::Ed25519(_) => ED25519_OID,
PublicKeyData::Ecdsa(_) => k256::elliptic_curve::ALGORITHM_OID,
},
}
}
#[must_use]
pub fn to_bytes_raw(&self) -> Vec<u8> {
match &self.0 {
PublicKeyData::Ed25519(key) => key.to_bytes().as_slice().to_vec(),
PublicKeyData::Ecdsa(key) => key.to_bytes().as_slice().to_vec(),
}
}
#[must_use]
pub fn to_string_der(&self) -> String {
hex::encode(self.to_bytes_der())
}
#[must_use]
pub fn to_string_raw(&self) -> String {
hex::encode(self.to_bytes_raw())
}
#[must_use]
pub fn to_account_id(&self, shard: u64, realm: u64) -> AccountId {
AccountId { shard, realm, alias: Some(*self), evm_address: None, num: 0, checksum: None }
}
pub fn to_evm_address(&self) -> crate::Result<String> {
if let PublicKeyData::Ecdsa(ecdsa_key) = &self.0 {
let hash = sha3::Keccak256::digest(ecdsa_key.to_bytes());
Ok(format!("0x{}", hex::encode(hash.get(12..32).unwrap())))
} else {
Err(Error::WrongKeyType {
task: "convert to evm address",
key_enum: "PublicKey",
key_variant: "Ed25519",
})
}
}
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> crate::Result<()> {
match &self.0 {
PublicKeyData::Ed25519(key) => {
let signature = ed25519_dalek::Signature::from_bytes(signature)
.map_err(Error::signature_verify)?;
key.verify(msg, &signature).map_err(Error::signature_verify)
}
PublicKeyData::Ecdsa(key) => {
let signature =
ecdsa::Signature::try_from(signature).map_err(Error::signature_verify)?;
key.verify_digest(sha3::Keccak256::new_with_prefix(msg), &signature)
.map_err(Error::signature_verify)
}
}
}
#[must_use]
pub(crate) fn kind(&self) -> super::KeyKind {
match &self.0 {
PublicKeyData::Ed25519(_) => super::KeyKind::Ed25519,
PublicKeyData::Ecdsa(_) => super::KeyKind::Ecdsa,
}
}
}
impl Debug for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "\"{self}\"")
}
}
impl Display for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad(&self.to_string_der())
}
}
impl FromStr for PublicKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_bytes(&hex::decode(s.strip_prefix("0x").unwrap_or(s)).map_err(Error::key_parse)?)
}
}
impl FromProtobuf<services::Key> for PublicKey {
fn from_protobuf(pb: services::Key) -> crate::Result<Self>
where
Self: Sized,
{
use services::key::Key::*;
match pb.key {
Some(Ed25519(bytes)) => PublicKey::from_bytes_ed25519(&bytes),
Some(ContractId(_)) => {
Err(Error::from_protobuf("unexpected unsupported Contract ID key in single key"))
}
Some(DelegatableContractId(_)) => Err(Error::from_protobuf(
"unexpected unsupported Delegatable Contract ID key in single key",
)),
Some(Rsa3072(_)) => {
Err(Error::from_protobuf("unexpected unsupported RSA-3072 key in single key"))
}
Some(Ecdsa384(_)) => {
Err(Error::from_protobuf("unexpected unsupported ECDSA-384 key in single key"))
}
Some(ThresholdKey(_)) => {
Err(Error::from_protobuf("unexpected threshold key as single key"))
}
Some(KeyList(_)) => Err(Error::from_protobuf("unexpected key list as single key")),
Some(EcdsaSecp256k1(bytes)) => PublicKey::from_bytes_ecdsa(&bytes),
None => Err(Error::from_protobuf("unexpected empty key in single key")),
}
}
}
impl ToProtobuf for PublicKey {
type Protobuf = services::Key;
fn to_protobuf(&self) -> Self::Protobuf {
let key = match self.kind() {
super::KeyKind::Ed25519 => services::key::Key::Ed25519(self.to_bytes_raw()),
super::KeyKind::Ecdsa => services::key::Key::EcdsaSecp256k1(self.to_bytes_raw()),
};
Self::Protobuf { key: Some(key) }
}
}