use std::fmt;
use serde::{Deserialize, Serialize};
use crate::port::SignerError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Scheme {
Secp256k1,
Ed25519,
}
impl fmt::Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Secp256k1 => "secp256k1",
Self::Ed25519 => "ed25519",
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum KeyRole {
Session,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct KeyRef(String);
impl KeyRef {
pub fn new(key_ref: impl Into<String>) -> Result<Self, SignerError> {
let key_ref = key_ref.into();
if key_ref.is_empty() || key_ref.len() > 256 {
return Err(SignerError::Invalid(
"a key reference is 1..=256 bytes".to_owned(),
));
}
Ok(Self(key_ref))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for KeyRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Subject {
pub venture: String,
pub user: Option<String>,
}
impl Subject {
pub fn new(venture: impl Into<String>, user: Option<String>) -> Result<Self, SignerError> {
let venture = venture.into();
if venture.is_empty() || venture.len() > 64 {
return Err(SignerError::Invalid("a venture is 1..=64 bytes".to_owned()));
}
Ok(Self { venture, user })
}
}
impl fmt::Display for Subject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.user {
Some(user) => write!(f, "{}/{user}", self.venture),
None => f.write_str(&self.venture),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeyInfo {
pub key_ref: KeyRef,
pub scheme: Scheme,
pub role: KeyRole,
pub label: String,
pub subject: Subject,
pub identity: String,
}
impl KeyInfo {
#[must_use]
pub fn key_ref(&self) -> &KeyRef {
&self.key_ref
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Signature {
Secp256k1 {
r: [u8; 32],
s: [u8; 32],
v: u8,
},
Ed25519 {
bytes: [u8; 64],
},
}
impl Signature {
#[must_use]
pub fn scheme(&self) -> Scheme {
match self {
Self::Secp256k1 { .. } => Scheme::Secp256k1,
Self::Ed25519 { .. } => Scheme::Ed25519,
}
}
}
impl Serialize for Signature {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
match self {
Self::Secp256k1 { r, s, v } => {
let mut out = serializer.serialize_struct("Signature", 4)?;
out.serialize_field("scheme", "secp256k1")?;
out.serialize_field("r", &format!("0x{}", hex::encode(r)))?;
out.serialize_field("s", &format!("0x{}", hex::encode(s)))?;
out.serialize_field("v", v)?;
out.end()
}
Self::Ed25519 { bytes } => {
let mut out = serializer.serialize_struct("Signature", 2)?;
out.serialize_field("scheme", "ed25519")?;
out.serialize_field("bytes", &bs58::encode(bytes).into_string())?;
out.end()
}
}
}
}
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(tag = "scheme", rename_all = "snake_case")]
enum Wire {
Secp256k1 { r: String, s: String, v: u8 },
Ed25519 { bytes: String },
}
let wire = Wire::deserialize(deserializer)?;
match wire {
Wire::Secp256k1 { r, s, v } => Ok(Self::Secp256k1 {
r: hex_word(&r, "r")?,
s: hex_word(&s, "s")?,
v,
}),
Wire::Ed25519 { bytes } => Ok(Self::Ed25519 {
bytes: base58_64(&bytes)?,
}),
}
}
}
fn hex_word<E: serde::de::Error>(text: &str, field: &str) -> Result<[u8; 32], E> {
let bytes = text
.strip_prefix("0x")
.and_then(|part| hex::decode(part).ok())
.ok_or_else(|| E::custom(format!("signature field `{field}` is not 0x-hex")))?;
bytes
.try_into()
.map_err(|_| E::custom(format!("signature field `{field}` is not 32 bytes")))
}
fn base58_64<E: serde::de::Error>(text: &str) -> Result<[u8; 64], E> {
let bytes = bs58::decode(text)
.into_vec()
.map_err(|_| E::custom("signature field `bytes` is not base58"))?;
bytes
.try_into()
.map_err(|_| E::custom("signature field `bytes` is not 64 bytes"))
}