commit e04c8821d54363e3930b270f4bd16b26b59cabbe
parent b8177459ab3c163d357dad1a1e396d15b6a66be6
Author: kernelkind <kernelkind@gmail.com>
Date: Tue, 7 May 2024 19:57:40 -0400
Add keypair & update pubkey
Keypair & FullKeypair match structs in damus ios
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
4 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/enostr/src/error.rs b/enostr/src/error.rs
@@ -8,8 +8,10 @@ pub enum Error {
Empty,
DecodeFailed,
HexDecodeFailed,
+ InvalidBech32,
InvalidByteSize,
InvalidSignature,
+ InvalidPublicKey,
// Secp(secp256k1::Error),
Json(serde_json::Error),
Generic(String),
@@ -23,6 +25,7 @@ impl std::cmp::PartialEq for Error {
(Error::HexDecodeFailed, Error::HexDecodeFailed) => true,
(Error::InvalidSignature, Error::InvalidSignature) => true,
(Error::InvalidByteSize, Error::InvalidByteSize) => true,
+ (Error::InvalidPublicKey, Error::InvalidPublicKey) => true,
// This is slightly wrong but whatevs
(Error::Json(..), Error::Json(..)) => true,
(Error::Generic(left), Error::Generic(right)) => left == right,
@@ -40,6 +43,8 @@ impl fmt::Display for Error {
Self::InvalidSignature => write!(f, "invalid signature"),
Self::HexDecodeFailed => write!(f, "hex decoding failed"),
Self::InvalidByteSize => write!(f, "invalid byte size"),
+ Self::InvalidBech32 => write!(f, "invalid bech32 string"),
+ Self::InvalidPublicKey => write!(f, "invalid public key"),
//Self::Secp(e) => write!(f, "{e}"),
Self::Json(e) => write!(f, "{e}"),
Self::Generic(e) => write!(f, "{e}"),
diff --git a/enostr/src/keypair.rs b/enostr/src/keypair.rs
@@ -0,0 +1,73 @@
+use crate::Pubkey;
+use crate::SecretKey;
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct Keypair {
+ pub pubkey: Pubkey,
+ pub secret_key: Option<SecretKey>,
+}
+
+impl Keypair {
+ pub fn new(secret_key: SecretKey) -> Self {
+ let cloned_secret_key = secret_key.clone();
+ let nostr_keys = nostr::Keys::new(secret_key);
+ Keypair {
+ pubkey: Pubkey::new(&nostr_keys.public_key().to_bytes()),
+ secret_key: Some(cloned_secret_key),
+ }
+ }
+
+ pub fn only_pubkey(pubkey: Pubkey) -> Self {
+ Keypair {
+ pubkey,
+ secret_key: None,
+ }
+ }
+
+ pub fn to_full(self) -> Option<FullKeypair> {
+ if let Some(secret_key) = self.secret_key {
+ Some(FullKeypair {
+ pubkey: self.pubkey,
+ secret_key,
+ })
+ } else {
+ None
+ }
+ }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct FullKeypair {
+ pub pubkey: Pubkey,
+ pub secret_key: SecretKey,
+}
+
+impl FullKeypair {
+ pub fn new(pubkey: Pubkey, secret_key: SecretKey) -> Self {
+ FullKeypair { pubkey, secret_key }
+ }
+}
+
+impl std::fmt::Display for Keypair {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Keypair:\n\tpublic: {}\n\tsecret: {}",
+ self.pubkey,
+ match self.secret_key {
+ Some(_) => "Some(<hidden>)",
+ None => "None",
+ }
+ )
+ }
+}
+
+impl std::fmt::Display for FullKeypair {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Keypair:\n\tpublic: {}\n\tsecret: {}",
+ self.pubkey, "<hidden>"
+ )
+ }
+}
diff --git a/enostr/src/lib.rs b/enostr/src/lib.rs
@@ -2,6 +2,7 @@ mod client;
mod error;
mod event;
mod filter;
+mod keypair;
mod profile;
mod pubkey;
mod relay;
@@ -11,6 +12,8 @@ pub use error::Error;
pub use event::{Event, EventId};
pub use ewebsock;
pub use filter::Filter;
+pub use keypair::{FullKeypair, Keypair};
+pub use nostr::SecretKey;
pub use profile::Profile;
pub use pubkey::Pubkey;
pub use relay::message::{RelayEvent, RelayMessage};
diff --git a/enostr/src/pubkey.rs b/enostr/src/pubkey.rs
@@ -1,14 +1,15 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::Error;
-use hex;
use log::debug;
-//use nostr::key::XOnlyPublicKey;
+use nostr::bech32::Hrp;
use std::fmt;
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct Pubkey([u8; 32]);
+static HRP_NPUB: Hrp = Hrp::parse_unchecked("npub");
+
impl Pubkey {
pub fn new(data: &[u8; 32]) -> Self {
Self(*data)
@@ -25,6 +26,41 @@ impl Pubkey {
pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
Ok(Pubkey(hex::decode(hex_str)?.as_slice().try_into()?))
}
+
+ pub fn try_from_hex_str_with_verify(hex_str: &str) -> Result<Self, Error> {
+ let vec: Vec<u8> = hex::decode(hex_str)?;
+ if vec.len() != 32 {
+ Err(Error::HexDecodeFailed)
+ } else {
+ let _ = match nostr::secp256k1::XOnlyPublicKey::from_slice(&vec) {
+ Ok(r) => Ok(r),
+ Err(_) => Err(Error::InvalidPublicKey),
+ }?;
+
+ Ok(Pubkey(vec.try_into().unwrap()))
+ }
+ }
+
+ pub fn try_from_bech32_string(s: &str, verify: bool) -> Result<Self, Error> {
+ let data = match nostr::bech32::decode(s) {
+ Ok(res) => Ok(res),
+ Err(_) => Err(Error::InvalidBech32),
+ }?;
+
+ if data.0 != HRP_NPUB {
+ Err(Error::InvalidBech32)
+ } else if data.1.len() != 32 {
+ Err(Error::InvalidByteSize)
+ } else {
+ if verify {
+ let _ = match nostr::secp256k1::XOnlyPublicKey::from_slice(&data.1) {
+ Ok(r) => Ok(r),
+ Err(_) => Err(Error::InvalidPublicKey),
+ }?;
+ }
+ Ok(Pubkey(data.1.try_into().unwrap()))
+ }
+ }
}
impl fmt::Display for Pubkey {