use k256::ecdsa::VerifyingKey;
use k256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
use k256::{AffinePoint, ProjectivePoint, Scalar};
use std::fmt;
use crate::ec::private_key::PrivateKey;
use crate::ec::signature::Signature;
use crate::hash::{hash160, sha256_hmac};
use crate::PrimitivesError;
const COMPRESSED_LEN: usize = 33;
const UNCOMPRESSED_LEN: usize = 65;
#[derive(Clone, Debug)]
pub struct PublicKey {
inner: VerifyingKey,
}
impl PublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PrimitivesError> {
if bytes.is_empty() {
return Err(PrimitivesError::InvalidPublicKey(
"pubkey string is empty".to_string(),
));
}
let vk = VerifyingKey::from_sec1_bytes(bytes)?;
Ok(PublicKey { inner: vk })
}
pub fn from_hex(hex_str: &str) -> Result<Self, PrimitivesError> {
let bytes = hex::decode(hex_str)?;
Self::from_bytes(&bytes)
}
pub fn to_compressed(&self) -> [u8; COMPRESSED_LEN] {
let point = self.inner.to_encoded_point(true);
let mut out = [0u8; COMPRESSED_LEN];
out.copy_from_slice(point.as_bytes());
out
}
pub fn to_uncompressed(&self) -> [u8; UNCOMPRESSED_LEN] {
let point = self.inner.to_encoded_point(false);
let mut out = [0u8; UNCOMPRESSED_LEN];
out.copy_from_slice(point.as_bytes());
out
}
pub fn to_hex(&self) -> String {
hex::encode(self.to_compressed())
}
pub fn to_der(&self) -> Vec<u8> {
self.to_compressed().to_vec()
}
pub fn to_der_hex(&self) -> String {
hex::encode(self.to_der())
}
pub fn hash160(&self) -> [u8; 20] {
hash160(&self.to_compressed())
}
pub fn to_address(&self) -> String {
let h = self.hash160();
let mut payload = Vec::with_capacity(25);
payload.push(0x00); payload.extend_from_slice(&h);
let checksum = crate::hash::sha256d(&payload);
payload.extend_from_slice(&checksum[..4]);
bs58::encode(payload).into_string()
}
pub fn verify(&self, hash: &[u8], sig: &Signature) -> bool {
sig.verify(hash, self)
}
pub fn derive_child(
&self,
private_key: &PrivateKey,
invoice_number: &str,
) -> Result<PublicKey, PrimitivesError> {
let shared_secret = self.derive_shared_secret(private_key)?;
let shared_compressed = shared_secret.to_compressed();
let hmac_result = sha256_hmac(&shared_compressed, invoice_number.as_bytes());
use k256::elliptic_curve::ops::Reduce;
let uint = k256::U256::from_be_slice(&hmac_result);
let hmac_scalar = <Scalar as Reduce<k256::U256>>::reduce(uint);
let new_point = ProjectivePoint::GENERATOR * hmac_scalar;
let self_point = self.to_projective_point()?;
let result_point = self_point + new_point;
let affine = result_point.to_affine();
let encoded = affine.to_encoded_point(true);
PublicKey::from_bytes(encoded.as_bytes())
}
pub fn derive_shared_secret(
&self,
priv_key: &PrivateKey,
) -> Result<PublicKey, PrimitivesError> {
priv_key.derive_shared_secret(self)
}
pub(crate) fn from_k256_verifying_key(vk: &VerifyingKey) -> Self {
PublicKey { inner: *vk }
}
pub(crate) fn to_projective_point(&self) -> Result<ProjectivePoint, PrimitivesError> {
let encoded = self.inner.to_encoded_point(false);
let ct_option = AffinePoint::from_encoded_point(&encoded);
if bool::from(ct_option.is_some()) {
Ok(ProjectivePoint::from(ct_option.unwrap()))
} else {
Err(PrimitivesError::PointNotOnCurve)
}
}
pub(crate) fn verifying_key(&self) -> &VerifyingKey {
&self.inner
}
}
impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool {
self.to_compressed() == other.to_compressed()
}
}
impl Eq for PublicKey {}
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pub_keys() {
struct PubKeyTest {
name: &'static str,
key: Vec<u8>,
is_valid: bool,
}
let tests = vec![
PubKeyTest {
name: "uncompressed ok",
key: vec![
0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, 0x49, 0x84, 0x0f,
0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84,
0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64,
0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3,
],
is_valid: true,
},
PubKeyTest {
name: "uncompressed x changed",
key: vec![
0x04, 0x15, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, 0x49, 0x84, 0x0f,
0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84,
0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64,
0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3,
],
is_valid: false,
},
PubKeyTest {
name: "compressed ok (ybit = 0)",
key: vec![
0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, 0x49, 0xfd, 0xd6, 0x75,
0xc9, 0x80, 0x75, 0xf1, 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21,
0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d,
],
is_valid: true,
},
PubKeyTest {
name: "compressed ok (ybit = 1)",
key: vec![
0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 0x09, 0xfb, 0x14, 0x3e, 0x0e,
0x8f, 0xe3, 0x96, 0x34, 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4,
0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
],
is_valid: true,
},
PubKeyTest {
name: "wrong length",
key: vec![0x05],
is_valid: false,
},
];
for test in &tests {
let result = PublicKey::from_bytes(&test.key);
if test.is_valid {
assert!(
result.is_ok(),
"{} pubkey should be valid but got error: {:?}",
test.name,
result.err()
);
} else {
assert!(
result.is_err(),
"{} pubkey should be invalid but was accepted",
test.name
);
}
}
}
#[test]
fn test_public_key_is_equal() {
let pk1 = PublicKey::from_bytes(&[
0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f,
0xe3, 0x96, 0x34, 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 0x7f, 0x5b,
0x2a, 0x4b, 0x7d, 0x44, 0x8e,
])
.unwrap();
let pk2 = PublicKey::from_bytes(&[
0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9,
0x80, 0x75, 0xf1, 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0xa9, 0xa1,
0xf4, 0x80, 0x9d, 0x3b, 0x4d,
])
.unwrap();
assert_eq!(pk1, pk1);
assert_ne!(pk1, pk2);
}
#[test]
fn test_compressed_round_trip() {
let original_bytes: [u8; 33] = [
0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9,
0x80, 0x75, 0xf1, 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0xa9, 0xa1,
0xf4, 0x80, 0x9d, 0x3b, 0x4d,
];
let pk = PublicKey::from_bytes(&original_bytes).unwrap();
let compressed = pk.to_compressed();
assert_eq!(compressed, original_bytes);
}
#[test]
fn test_display() {
let pk = PublicKey::from_bytes(&[
0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9,
0x80, 0x75, 0xf1, 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0xa9, 0xa1,
0xf4, 0x80, 0x9d, 0x3b, 0x4d,
])
.unwrap();
assert_eq!(
format!("{}", pk),
"02ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"
);
}
#[test]
fn test_to_der_hex() {
let pk = PublicKey::from_bytes(&[
0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9,
0x80, 0x75, 0xf1, 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0xa9, 0xa1,
0xf4, 0x80, 0x9d, 0x3b, 0x4d,
])
.unwrap();
assert_eq!(pk.to_der_hex(), pk.to_hex());
}
#[test]
fn test_brc42_public_vectors() {
let vectors_json = include_str!("testdata/BRC42.public.vectors.json");
let vectors: Vec<serde_json::Value> = serde_json::from_str(vectors_json).unwrap();
for (i, v) in vectors.iter().enumerate() {
let sender_priv_hex = v["senderPrivateKey"].as_str().unwrap();
let recipient_pub_hex = v["recipientPublicKey"].as_str().unwrap();
let invoice_number = v["invoiceNumber"].as_str().unwrap();
let expected_pub_hex = v["publicKey"].as_str().unwrap();
let private_key = PrivateKey::from_hex(sender_priv_hex)
.unwrap_or_else(|e| panic!("vector #{}: parse priv key: {}", i + 1, e));
let public_key = PublicKey::from_hex(recipient_pub_hex)
.unwrap_or_else(|e| panic!("vector #{}: parse pub key: {}", i + 1, e));
let derived = public_key
.derive_child(&private_key, invoice_number)
.unwrap_or_else(|e| panic!("vector #{}: derive child: {}", i + 1, e));
let derived_hex = derived.to_hex();
assert_eq!(
derived_hex,
expected_pub_hex,
"BRC42 public vector #{}: derived key mismatch",
i + 1
);
}
}
}