use bitcoin::hashes::{sha256, Hash, HashEngine};
use bitcoin::secp256k1::{PublicKey, Scalar, SecretKey};
use crate::block::secp;
pub const N: [u8; 32] = bitcoin::secp256k1::constants::CURVE_ORDER;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum KeyError {
#[error("a secret is a scalar in [1, n-1]")]
Secret,
#[error("a tweak is a scalar in [1, n-1]")]
Tweak,
#[error("not a compressed secp256k1 point (02/03 + x, on the curve)")]
NotAPoint,
#[error("that x is not on the curve")]
OffCurve,
#[error("not a did:nostr identifier, a did:nostr Multikey, or a compressed point")]
NotAnIdentifier,
#[error("the tweak cancels the secret")]
Cancels,
#[error("the tweak lands on infinity")]
Infinity,
#[error("the tweak is zero: refuse this data")]
ZeroTweak,
#[error("{0} is not hex")]
NotHex(&'static str),
}
pub type Result<T> = core::result::Result<T, KeyError>;
fn int32(text: &str, what: &'static str) -> Result<Option<[u8; 32]>> {
let bytes = hex::decode(text).map_err(|_| KeyError::NotHex(what))?;
let first = bytes.iter().position(|b| *b != 0).unwrap_or(bytes.len());
let significant = &bytes[first..];
if significant.len() > 32 {
return Ok(None);
}
let mut out = [0u8; 32];
out[32 - significant.len()..].copy_from_slice(significant);
Ok(Some(out))
}
fn secret(d: &str) -> Result<SecretKey> {
int32(d, "a secret")?
.and_then(|b| SecretKey::from_slice(&b).ok())
.ok_or(KeyError::Secret)
}
fn scalar(t: &str) -> Result<Scalar> {
let b = int32(t, "a tweak")?.ok_or(KeyError::Tweak)?;
if b == [0u8; 32] {
return Err(KeyError::Tweak);
}
Scalar::from_be_bytes(b).map_err(|_| KeyError::Tweak)
}
fn is_lower_hex(s: &str, len: usize) -> bool {
s.len() == len
&& s.bytes()
.all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(&c))
}
pub fn is_point(p: &str) -> bool {
parse_point(p).is_ok()
}
pub fn parse_point(p: &str) -> Result<PublicKey> {
if !is_lower_hex(p, 66) || !(p.starts_with("02") || p.starts_with("03")) {
return Err(KeyError::NotAPoint);
}
let bytes = hex::decode(p).map_err(|_| KeyError::NotAPoint)?;
PublicKey::from_slice(&bytes).map_err(|_| KeyError::NotAPoint)
}
fn point_hex(p: &PublicKey) -> String {
hex::encode(p.serialize())
}
pub fn public_key(d: &str) -> Result<String> {
Ok(point_hex(&PublicKey::from_secret_key(secp(), &secret(d)?)))
}
pub fn normalize(d: &str) -> Result<String> {
let sk = secret(d)?;
if PublicKey::from_secret_key(secp(), &sk).serialize()[0] == 0x03 {
Ok(hex::encode(sk.negate().secret_bytes()))
} else {
Ok(d.to_lowercase())
}
}
pub fn signing_key(d: &str) -> Result<String> {
normalize(d)
}
pub fn base_point(id: &str) -> Result<String> {
let s = id.trim().to_lowercase();
let x = s.strip_prefix("did:nostr:").unwrap_or(&s);
if is_lower_hex(x, 64) {
let even = format!("02{x}");
let bytes = hex::decode(&even).map_err(|_| KeyError::OffCurve)?;
PublicKey::from_slice(&bytes).map_err(|_| KeyError::OffCurve)?;
parse_point(&even)?;
return Ok(even);
}
if let Some(mk) = s.strip_prefix("fe701") {
if is_lower_hex(mk, 66) && (mk.starts_with("02") || mk.starts_with("03")) {
parse_point(mk)?;
return Ok(mk.to_string());
}
}
if is_lower_hex(&s, 66) && (s.starts_with("02") || s.starts_with("03")) {
parse_point(&s)?;
return Ok(s);
}
Err(KeyError::NotAnIdentifier)
}
pub fn x_only(p: &str) -> Result<String> {
parse_point(p)?;
Ok(p[2..].to_string())
}
pub fn did(p: &str) -> Result<String> {
Ok(format!("did:nostr:{}", x_only(p)?))
}
pub fn multikey(p: &str) -> Result<String> {
parse_point(p)?;
Ok(format!("fe701{p}"))
}
pub fn negate(p: &str) -> Result<String> {
parse_point(p)?;
let prefix = if p.starts_with("02") { "03" } else { "02" };
Ok(format!("{prefix}{}", &p[2..]))
}
pub fn tweak_secret(d: &str, t: &str) -> Result<String> {
let sk = secret(d)?;
let t = scalar(t)?;
sk.add_tweak(&t)
.map(|r| hex::encode(r.secret_bytes()))
.map_err(|_| KeyError::Cancels)
}
pub fn tweak_point(p: &str, t: &str) -> Result<String> {
let point = parse_point(p)?;
let t = scalar(t)?;
point
.add_exp_tweak(secp(), &t)
.map(|q| point_hex(&q))
.map_err(|_| KeyError::Infinity)
}
pub fn chain_points<T: AsRef<str>>(p: &str, tweaks: &[T]) -> Result<Vec<String>> {
parse_point(p)?;
let mut out = vec![p.to_string()];
for t in tweaks {
let next = tweak_point(out.last().expect("never empty"), t.as_ref())?;
out.push(next);
}
Ok(out)
}
pub fn chain_secrets<T: AsRef<str>>(d: &str, tweaks: &[T]) -> Result<Vec<String>> {
secret(d)?;
let mut out = vec![d.to_lowercase()];
for t in tweaks {
let next = tweak_secret(out.last().expect("never empty"), t.as_ref())?;
out.push(next);
}
Ok(out)
}
fn reduce_mod_n(v: [u8; 32]) -> [u8; 32] {
if Scalar::from_be_bytes(v).is_ok() {
return v;
}
let mut out = [0u8; 32];
let mut borrow = 0i16;
for i in (0..32).rev() {
let mut x = i16::from(v[i]) - i16::from(N[i]) - borrow;
borrow = 0;
if x < 0 {
x += 256;
borrow = 1;
}
out[i] = x as u8;
}
out
}
pub fn tagged_scalar(tag: &str, chunks: &[&[u8]]) -> Result<String> {
let t = sha256::Hash::hash(tag.as_bytes()).to_byte_array();
let mut e = sha256::Hash::engine();
e.input(&t);
e.input(&t);
for c in chunks {
e.input(c);
}
let n = reduce_mod_n(sha256::Hash::from_engine(e).to_byte_array());
if n == [0u8; 32] {
return Err(KeyError::ZeroTweak);
}
Ok(hex::encode(n))
}
pub fn tagged_scalar_hex<T: AsRef<str>>(tag: &str, chunks: &[T]) -> Result<String> {
let bytes = chunks
.iter()
.map(|c| hex::decode(c.as_ref()).map_err(|_| KeyError::NotHex("a data chunk")))
.collect::<Result<Vec<_>>>()?;
let refs: Vec<&[u8]> = bytes.iter().map(Vec::as_slice).collect();
tagged_scalar(tag, &refs)
}
pub fn tap_tweak(p: &str, h32: Option<&str>) -> Result<String> {
let x = x_only(p)?;
match h32 {
Some(h) => tagged_scalar_hex("TapTweak", &[x.as_str(), h]),
None => tagged_scalar_hex("TapTweak", &[x.as_str()]),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reduction_subtracts_n_once() {
let mut v = N;
v[31] += 5;
let mut five = [0u8; 32];
five[31] = 5;
assert_eq!(reduce_mod_n(v), five);
assert_eq!(reduce_mod_n(N), [0u8; 32]);
assert_eq!(reduce_mod_n(five), five);
let max = reduce_mod_n([0xff; 32]);
assert!(Scalar::from_be_bytes(max).is_ok());
}
#[test]
fn hex_integers_of_any_length() {
assert_eq!(
public_key("01").unwrap(),
public_key(&format!("{:064x}", 1)).unwrap()
);
assert_eq!(
public_key(&format!("00{}", "11".repeat(32))).unwrap(),
public_key(&"11".repeat(32)).unwrap()
);
assert_eq!(public_key(&"ff".repeat(33)), Err(KeyError::Secret));
assert_eq!(public_key("zz"), Err(KeyError::NotHex("a secret")));
assert_eq!(
tweak_point(
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"1z"
),
Err(KeyError::NotHex("a tweak"))
);
}
#[test]
fn infinity_is_refused() {
let mut n1 = N;
n1[31] -= 1;
let g = public_key("01").unwrap();
assert_eq!(tweak_point(&g, &hex::encode(n1)), Err(KeyError::Infinity));
}
}