clone

a self-hosted sync node for lofi apps: jazz sync over iroh, ticket-gated, with no static ip or cloud needed

feat/scope-down-ticket-exchange
lofi-node / native / iroh-js / src / key.rs
key.rs 4.4K
1use std::str::FromStr;23use napi::bindgen_prelude::*;4use napi_derive::napi;56/// An endpoint's identifier, a 32-byte ed25519 public key.7#[derive(Debug, Clone, Eq)]8#[napi]9pub struct EndpointId {10 key: [u8; 32],11}1213impl From<iroh::EndpointId> for EndpointId {14 fn from(key: iroh::EndpointId) -> Self {15 EndpointId {16 key: *key.as_bytes(),17 }18 }19}2021impl From<&EndpointId> for iroh::EndpointId {22 fn from(key: &EndpointId) -> Self {23 iroh::EndpointId::from_bytes(&key.key).expect("EndpointId bytes are always valid")24 }25}2627impl EndpointId {28 pub(crate) fn raw_bytes(&self) -> [u8; 32] {29 self.key30 }3132 pub(crate) fn from_raw_bytes(key: [u8; 32]) -> Self {33 EndpointId { key }34 }35}3637#[napi]38impl EndpointId {39 /// Returns true if both [`EndpointId`]s are equal.40 #[napi]41 pub fn equals(&self, other: &EndpointId) -> bool {42 *self == *other43 }4445 /// Get the underlying 32 bytes.46 #[napi]47 pub fn to_bytes(&self) -> Vec<u8> {48 self.key.to_vec()49 }5051 /// Parse an [`EndpointId`] from its base32 representation.52 #[napi(factory)]53 pub fn from_string(s: String) -> Result<Self> {54 let key = iroh::EndpointId::from_str(&s).map_err(anyhow::Error::from)?;55 Ok(key.into())56 }5758 /// Construct an [`EndpointId`] from raw bytes (32 bytes).59 #[napi(factory)]60 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {61 let bytes: [u8; 32] = bytes62 .try_into()63 .map_err(|_| anyhow::anyhow!("EndpointId requires exactly 32 bytes"))?;64 let key = iroh::EndpointId::from_bytes(&bytes).map_err(anyhow::Error::from)?;65 Ok(key.into())66 }6768 /// Short base32 prefix.69 #[napi]70 pub fn fmt_short(&self) -> String {71 iroh::EndpointId::from(self).fmt_short().to_string()72 }7374 /// Base32 string form.75 #[napi]76 pub fn to_string(&self) -> String {77 iroh::EndpointId::from(self).to_string()78 }7980 /// Verify a signature on `message` against this endpoint's key.81 #[napi]82 pub fn verify(&self, message: Vec<u8>, signature: &Signature) -> Result<()> {83 iroh::EndpointId::from(self)84 .verify(&message, &signature.0)85 .map_err(|e| anyhow::anyhow!("signature verification failed: {e:?}").into())86 }87}8889impl PartialEq for EndpointId {90 fn eq(&self, other: &EndpointId) -> bool {91 self.key == other.key92 }93}9495/// The secret key half of an endpoint identity.96#[derive(Debug, Clone)]97#[napi]98pub struct SecretKey(pub(crate) iroh::SecretKey);99100impl From<iroh::SecretKey> for SecretKey {101 fn from(key: iroh::SecretKey) -> Self {102 SecretKey(key)103 }104}105106#[napi]107impl SecretKey {108 /// Generate a new random secret key.109 #[napi(factory)]110 pub fn generate() -> Self {111 SecretKey(iroh::SecretKey::generate())112 }113114 /// Construct from raw bytes (32 bytes).115 #[napi(factory)]116 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {117 let bytes: [u8; 32] = bytes118 .try_into()119 .map_err(|_| anyhow::anyhow!("SecretKey requires exactly 32 bytes"))?;120 Ok(SecretKey(iroh::SecretKey::from_bytes(&bytes)))121 }122123 /// Raw 32-byte form.124 #[napi]125 pub fn to_bytes(&self) -> Vec<u8> {126 self.0.to_bytes().to_vec()127 }128129 /// The public [`EndpointId`] derived from this secret key.130 #[napi]131 pub fn public(&self) -> EndpointId {132 self.0.public().into()133 }134135 /// Sign a message, producing an ed25519 signature.136 #[napi]137 pub fn sign(&self, message: Vec<u8>) -> Signature {138 Signature(self.0.sign(&message))139 }140}141142/// An ed25519 signature over a message.143#[derive(Debug, Clone)]144#[napi]145pub struct Signature(pub(crate) iroh_base::Signature);146147#[napi]148impl Signature {149 /// Construct from raw bytes (64 bytes).150 #[napi(factory)]151 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {152 let bytes: [u8; 64] = bytes153 .try_into()154 .map_err(|_| anyhow::anyhow!("Signature requires exactly 64 bytes"))?;155 Ok(Signature(iroh_base::Signature::from_bytes(&bytes)))156 }157158 /// Raw 64-byte form.159 #[napi]160 pub fn to_bytes(&self) -> Vec<u8> {161 self.0.to_bytes().to_vec()162 }163164 /// Lowercase hex representation.165 #[napi]166 pub fn to_string(&self) -> String {167 self.0168 .to_bytes()169 .iter()170 .map(|b| format!("{b:02x}"))171 .collect()172 }173}