use alloc::boxed::Box;
use core::cmp::Ordering;
use core::convert::Infallible;
use core::fmt;
use core::hash::{Hash, Hasher};
#[cfg(feature = "std")]
use core::str::FromStr;
#[cfg(all(feature = "std", feature = "os-rng"))]
use rand::rand_core::UnwrapErr;
#[cfg(all(feature = "std", feature = "os-rng"))]
use rand::rngs::SysRng;
#[cfg(feature = "rand")]
use rand::{CryptoRng, Rng};
use secp256k1::schnorr::Signature;
use secp256k1::{self, Keypair, Message, Secp256k1, Signing, XOnlyPublicKey};
mod public_key;
mod secret_key;
pub use self::public_key::*;
pub use self::secret_key::*;
use crate::error::Error;
#[cfg(all(feature = "std", feature = "os-rng"))]
use crate::event::{AsyncSignEvent, Event, EventId, SignEvent, UnsignedEvent};
#[cfg(all(feature = "std", feature = "os-rng", feature = "nip04"))]
use crate::nips::nip04::{AsyncNip04, Nip04};
#[cfg(all(feature = "std", feature = "os-rng", feature = "nip44"))]
use crate::nips::nip44::{AsyncNip44, Nip44};
#[cfg(feature = "rand")]
use crate::util;
use crate::util::BoxedFuture;
#[cfg(feature = "std")]
use crate::util::SECP256K1;
#[derive(Clone)]
pub struct Keys {
pub public_key: PublicKey,
secret_key: SecretKey,
keypair: Keypair,
}
impl fmt::Debug for Keys {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Keys")
.field("public_key", &self.public_key)
.finish()
}
}
impl PartialEq for Keys {
fn eq(&self, other: &Self) -> bool {
self.public_key == other.public_key
}
}
impl Eq for Keys {}
impl PartialOrd for Keys {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Keys {
fn cmp(&self, other: &Self) -> Ordering {
self.public_key.cmp(&other.public_key)
}
}
impl Hash for Keys {
fn hash<H: Hasher>(&self, state: &mut H) {
self.public_key.hash(state)
}
}
impl Keys {
#[inline]
#[cfg(feature = "std")]
pub fn new(secret_key: SecretKey) -> Self {
Self::new_with_ctx(&SECP256K1, secret_key)
}
pub fn new_with_ctx<C>(secp: &Secp256k1<C>, secret_key: SecretKey) -> Self
where
C: Signing,
{
let keypair: Keypair = Keypair::from_secret_key(secp, &secret_key);
let public_key: XOnlyPublicKey = XOnlyPublicKey::from_keypair(&keypair).0;
Self {
public_key: PublicKey::from(public_key),
secret_key,
keypair,
}
}
#[inline]
#[cfg(feature = "std")]
pub fn parse(secret_key: &str) -> Result<Self, Error> {
Self::parse_with_ctx(&SECP256K1, secret_key)
}
#[inline]
pub fn parse_with_ctx<C>(secp: &Secp256k1<C>, secret_key: &str) -> Result<Self, Error>
where
C: Signing,
{
let secret_key: SecretKey = SecretKey::parse(secret_key)?;
Ok(Self::new_with_ctx(secp, secret_key))
}
#[inline]
#[cfg(all(feature = "std", feature = "os-rng"))]
pub fn generate() -> Self {
Self::generate_with_rng(&SECP256K1, &mut UnwrapErr(SysRng))
}
#[inline]
#[cfg(feature = "rand")]
pub fn generate_with_rng<C, R>(secp: &Secp256k1<C>, rng: &mut R) -> Self
where
C: Signing,
R: Rng,
{
let secret_key: SecretKey = SecretKey::generate_with_rng(rng);
Self::new_with_ctx(secp, secret_key)
}
#[inline]
pub fn public_key(&self) -> PublicKey {
self.public_key
}
#[inline]
pub fn secret_key(&self) -> &SecretKey {
&self.secret_key
}
#[inline]
#[cfg(all(feature = "std", feature = "os-rng"))]
pub fn sign_schnorr(&self, message: &Message) -> Signature {
self.sign_schnorr_with_rng(&SECP256K1, message, &mut UnwrapErr(SysRng))
}
#[cfg(feature = "rand")]
pub fn sign_schnorr_with_rng<C, R>(
&self,
secp: &Secp256k1<C>,
message: &Message,
rng: &mut R,
) -> Signature
where
C: Signing,
R: Rng + CryptoRng,
{
let aux: [u8; 32] = util::random_32_bytes(rng);
self.sign_schnorr_with_aux_rand(secp, message, &aux)
}
pub fn sign_schnorr_with_aux_rand<C>(
&self,
secp: &Secp256k1<C>,
message: &Message,
aux: &[u8; 32],
) -> Signature
where
C: Signing,
{
secp.sign_schnorr_with_aux_rand(message, &self.keypair, aux)
}
}
#[cfg(feature = "std")]
impl FromStr for Keys {
type Err = Error;
#[inline]
fn from_str(secret_key: &str) -> Result<Self, Self::Err> {
Self::parse(secret_key)
}
}
impl Drop for Keys {
#[inline]
fn drop(&mut self) {
self.keypair.non_secure_erase();
}
}
impl GetPublicKey for Keys {
type Error = Infallible;
#[inline]
fn get_public_key(&self) -> Result<PublicKey, Self::Error> {
Ok(self.public_key)
}
}
#[cfg(all(feature = "std", feature = "os-rng"))]
impl SignEvent for Keys {
type Error = Error;
fn sign_event(&self, unsigned: UnsignedEvent) -> Result<Event, Self::Error> {
let id: EventId = unsigned.id.unwrap_or_else(|| unsigned.compute_id());
let message: Message = Message::from_digest(id.to_bytes());
let sig: Signature = self.sign_schnorr(&message);
unsigned.add_signature(sig)
}
}
#[cfg(all(feature = "std", feature = "os-rng", feature = "nip04"))]
impl Nip04 for Keys {
type Error = Error;
fn nip04_encrypt(&self, public_key: &PublicKey, content: &str) -> Result<String, Self::Error> {
let secret_key: &SecretKey = self.secret_key();
crate::nips::nip04::encrypt(secret_key, public_key, content)
}
fn nip04_decrypt(
&self,
public_key: &PublicKey,
encrypted_content: &str,
) -> Result<String, Self::Error> {
let secret_key: &SecretKey = self.secret_key();
crate::nips::nip04::decrypt(secret_key, public_key, encrypted_content)
}
}
#[cfg(all(feature = "std", feature = "os-rng", feature = "nip44"))]
impl Nip44 for Keys {
type Error = Error;
fn nip44_encrypt(&self, public_key: &PublicKey, content: &str) -> Result<String, Self::Error> {
use crate::nips::nip44::{self, Version};
let secret_key: &SecretKey = self.secret_key();
nip44::encrypt(secret_key, public_key, content, Version::default())
}
fn nip44_decrypt(&self, public_key: &PublicKey, payload: &str) -> Result<String, Self::Error> {
let secret_key: &SecretKey = self.secret_key();
crate::nips::nip44::decrypt(secret_key, public_key, payload)
}
}
impl AsyncGetPublicKey for Keys {
type Error = Infallible;
fn get_public_key_async(&self) -> BoxedFuture<'_, Result<PublicKey, Self::Error>> {
Box::pin(async move { GetPublicKey::get_public_key(self) })
}
}
#[cfg(all(feature = "std", feature = "os-rng"))]
impl AsyncSignEvent for Keys {
type Error = Error;
fn sign_event_async(
&self,
unsigned: UnsignedEvent,
) -> BoxedFuture<'_, Result<Event, Self::Error>> {
Box::pin(async move { SignEvent::sign_event(self, unsigned) })
}
}
#[cfg(all(feature = "std", feature = "os-rng", feature = "nip04"))]
impl AsyncNip04 for Keys {
type Error = Error;
fn nip04_encrypt_async<'a>(
&'a self,
public_key: &'a PublicKey,
content: &'a str,
) -> BoxedFuture<'a, Result<String, Self::Error>> {
Box::pin(async move { Nip04::nip04_encrypt(self, public_key, content) })
}
fn nip04_decrypt_async<'a>(
&'a self,
public_key: &'a PublicKey,
encrypted_content: &'a str,
) -> BoxedFuture<'a, Result<String, Self::Error>> {
Box::pin(async move { Nip04::nip04_decrypt(self, public_key, encrypted_content) })
}
}
#[cfg(all(feature = "std", feature = "os-rng", feature = "nip44"))]
impl AsyncNip44 for Keys {
type Error = Error;
fn nip44_encrypt_async<'a>(
&'a self,
public_key: &'a PublicKey,
content: &'a str,
) -> BoxedFuture<'a, Result<String, Self::Error>> {
Box::pin(async move { Nip44::nip44_encrypt(self, public_key, content) })
}
fn nip44_decrypt_async<'a>(
&'a self,
public_key: &'a PublicKey,
payload: &'a str,
) -> BoxedFuture<'a, Result<String, Self::Error>> {
Box::pin(async move { Nip44::nip44_decrypt(self, public_key, payload) })
}
}
#[cfg(test)]
#[cfg(feature = "std")]
mod tests {
use super::*;
use crate::error::ErrorKind;
const SECRET_KEY_BECH32: &str =
"nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99";
const SECRET_KEY_HEX: &str = "6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e";
#[test]
fn parse_keys() -> Result<(), Error> {
Keys::parse(SECRET_KEY_BECH32)?;
Keys::parse(SECRET_KEY_HEX)?;
Ok(())
}
#[test]
fn parse_invalid_keys() {
assert_eq!(
Keys::parse("nsec...").unwrap_err().kind(),
ErrorKind::Invalid
);
assert_eq!(
Keys::parse("npub14f8usejl26twx0dhuxjh9cas7keav9vr0v8nvtwtrjqx3vycc76qqh9nsy")
.unwrap_err()
.kind(),
ErrorKind::Invalid
);
assert_eq!(
Keys::parse("6b911fd37cdf5c8").unwrap_err().kind(),
ErrorKind::Invalid
);
}
}
#[cfg(bench)]
#[cfg(all(feature = "std", feature = "os-rng"))]
mod benches {
use test::{Bencher, black_box};
use super::*;
#[bench]
pub fn generate_keys(bh: &mut Bencher) {
bh.iter(|| {
black_box(Keys::generate());
});
}
}