use crate::encoding::{hex, wif};
use crate::error::PrivateKeyError;
use crate::network::Network;
use rand::{rngs::OsRng, CryptoRng, RngCore};
use secp256k1::{Secp256k1, SecretKey};
use std::fmt;
use zeroize::Zeroize;
pub struct PrivateKeyIterator<R: RngCore + CryptoRng> {
rng: R,
secp: Secp256k1<secp256k1::All>,
}
impl PrivateKeyIterator<OsRng> {
fn new() -> Self {
Self {
rng: OsRng,
secp: Secp256k1::new(),
}
}
}
impl<R: RngCore + CryptoRng> PrivateKeyIterator<R> {
fn with_rng(rng: R) -> Self {
Self {
rng,
secp: Secp256k1::new(),
}
}
}
impl<R: RngCore + CryptoRng> Iterator for PrivateKeyIterator<R> {
type Item = PrivateKey;
fn next(&mut self) -> Option<Self::Item> {
let (secret_key, _) = self.secp.generate_keypair(&mut self.rng);
Some(PrivateKey { inner: secret_key })
}
}
pub struct PrivateKey {
inner: SecretKey,
}
impl PrivateKey {
pub fn random() -> Self {
let secp = Secp256k1::new();
let (secret_key, _) = secp.generate_keypair(&mut OsRng);
Self { inner: secret_key }
}
pub fn batch() -> PrivateKeyIterator<OsRng> {
PrivateKeyIterator::new()
}
pub fn batch_with_rng<R: RngCore + CryptoRng>(rng: R) -> PrivateKeyIterator<R> {
PrivateKeyIterator::with_rng(rng)
}
pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, PrivateKeyError> {
let secret_key = SecretKey::from_slice(&bytes).map_err(|_| PrivateKeyError::OutOfRange)?;
Ok(Self { inner: secret_key })
}
pub fn is_valid(bytes: &[u8; 32]) -> bool {
SecretKey::from_slice(bytes).is_ok()
}
pub fn from_hex(hex_str: &str) -> Result<Self, PrivateKeyError> {
if hex_str.len() != 64 {
return Err(PrivateKeyError::InvalidLength(hex_str.len() / 2));
}
let bytes = hex::decode(hex_str).map_err(|e| PrivateKeyError::InvalidHex(e.to_string()))?;
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Self::from_bytes(arr)
}
pub fn from_wif(wif_str: &str) -> Result<Self, PrivateKeyError> {
let (bytes, _network, _compressed) = wif::decode(wif_str).map_err(|e| match e {
wif::WifError::InvalidChecksum => PrivateKeyError::InvalidChecksum,
other => PrivateKeyError::InvalidWif(other.to_string()),
})?;
Self::from_bytes(bytes)
}
pub fn to_bytes(&self) -> [u8; 32] {
self.inner.secret_bytes()
}
pub fn to_hex(&self) -> String {
hex::encode(&self.to_bytes())
}
pub fn to_wif(&self, network: Network) -> String {
wif::encode(&self.to_bytes(), network, true)
}
pub fn to_decimal(&self) -> String {
let bytes = self.to_bytes();
bytes_to_decimal(&bytes)
}
pub fn public_key(&self) -> crate::public_key::PublicKey {
crate::public_key::PublicKey::from_private_key(self)
}
}
fn bytes_to_decimal(bytes: &[u8; 32]) -> String {
if bytes.iter().all(|&b| b == 0) {
return "0".to_string();
}
let mut result = Vec::new();
let mut temp = bytes.to_vec();
while temp.iter().any(|&b| b != 0) {
let mut remainder = 0u32;
for byte in temp.iter_mut() {
let value = (remainder << 8) | (*byte as u32);
*byte = (value / 10) as u8;
remainder = value % 10;
}
result.push((remainder as u8) + b'0');
}
result.reverse();
String::from_utf8(result).unwrap_or_else(|_| "0".to_string())
}
impl Drop for PrivateKey {
fn drop(&mut self) {
let mut bytes = self.inner.secret_bytes();
bytes.zeroize();
}
}
impl fmt::Debug for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"PrivateKey {{ hex: {}, decimal: {} }}",
self.to_hex(),
self.to_decimal()
)
}
}
impl Clone for PrivateKey {
fn clone(&self) -> Self {
Self { inner: self.inner }
}
}
impl PartialEq for PrivateKey {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl Eq for PrivateKey {}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn valid_private_key_bytes() -> impl Strategy<Value = [u8; 32]> {
prop::array::uniform32(any::<u8>()).prop_filter("must be valid secp256k1 key", |bytes| {
PrivateKey::is_valid(bytes)
})
}
#[test]
fn property_random_key_validity() {
for _ in 0..100 {
let key = PrivateKey::random();
let bytes = key.to_bytes();
assert!(PrivateKey::is_valid(&bytes), "Random key should be valid");
assert!(
bytes.iter().any(|&b| b != 0),
"Random key should not be all zeros"
);
let reconstructed =
PrivateKey::from_bytes(bytes).expect("Should be able to reconstruct from bytes");
assert_eq!(
key, reconstructed,
"Reconstructed key should equal original"
);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn property_hex_roundtrip(bytes in valid_private_key_bytes()) {
let key = PrivateKey::from_bytes(bytes).unwrap();
let hex = key.to_hex();
let recovered = PrivateKey::from_hex(&hex).unwrap();
prop_assert_eq!(key, recovered);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn property_bytes_roundtrip(bytes in valid_private_key_bytes()) {
let key = PrivateKey::from_bytes(bytes).unwrap();
let exported = key.to_bytes();
let recovered = PrivateKey::from_bytes(exported).unwrap();
prop_assert_eq!(key, recovered);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn property_wif_roundtrip(
bytes in valid_private_key_bytes(),
use_mainnet in any::<bool>()
) {
let network = if use_mainnet { Network::Mainnet } else { Network::Testnet };
let key = PrivateKey::from_bytes(bytes).unwrap();
let wif = key.to_wif(network);
let recovered = PrivateKey::from_wif(&wif).unwrap();
prop_assert_eq!(key, recovered);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn property_hex_case_insensitivity(bytes in valid_private_key_bytes()) {
let key = PrivateKey::from_bytes(bytes).unwrap();
let hex_lower = key.to_hex();
let hex_upper = hex_lower.to_uppercase();
let from_lower = PrivateKey::from_hex(&hex_lower).unwrap();
let from_upper = PrivateKey::from_hex(&hex_upper).unwrap();
prop_assert_eq!(from_lower, from_upper);
}
}
#[test]
fn property_invalid_input_rejection() {
let zero_bytes = [0u8; 32];
assert!(
!PrivateKey::is_valid(&zero_bytes),
"Zero key should be invalid"
);
assert!(
PrivateKey::from_bytes(zero_bytes).is_err(),
"Zero key should fail construction"
);
let curve_order: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C,
0xD0, 0x36, 0x41, 0x41,
];
assert!(
!PrivateKey::is_valid(&curve_order),
"Curve order should be invalid"
);
assert!(
PrivateKey::from_bytes(curve_order).is_err(),
"Curve order should fail construction"
);
let above_order: [u8; 32] = [0xFF; 32];
assert!(
!PrivateKey::is_valid(&above_order),
"Value above curve order should be invalid"
);
assert!(
PrivateKey::from_bytes(above_order).is_err(),
"Value above curve order should fail construction"
);
let max_valid: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C,
0xD0, 0x36, 0x41, 0x40,
];
assert!(PrivateKey::is_valid(&max_valid), "n-1 should be valid");
assert!(
PrivateKey::from_bytes(max_valid).is_ok(),
"n-1 should succeed construction"
);
let min_valid: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
];
assert!(PrivateKey::is_valid(&min_valid), "1 should be valid");
assert!(
PrivateKey::from_bytes(min_valid).is_ok(),
"1 should succeed construction"
);
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn property_debug_output_format(bytes in valid_private_key_bytes()) {
let key = PrivateKey::from_bytes(bytes).unwrap();
let debug_output = format!("{:?}", key);
let hex_output = key.to_hex();
let decimal_output = key.to_decimal();
let expected = format!("PrivateKey {{ hex: {}, decimal: {} }}", hex_output, decimal_output);
prop_assert_eq!(&debug_output, &expected);
}
}
#[test]
fn test_decimal_conversion() {
let key = PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000000001",
)
.unwrap();
assert_eq!(key.to_decimal(), "1");
let key = PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000000100",
)
.unwrap();
assert_eq!(key.to_decimal(), "256");
let key = PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000010000",
)
.unwrap();
assert_eq!(key.to_decimal(), "65536");
}
#[test]
fn test_vector_wif_bitcoin_wiki() {
let hex = "0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D";
let expected_wif_uncompressed = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ";
let key = PrivateKey::from_hex(hex).unwrap();
let from_wif = PrivateKey::from_wif(expected_wif_uncompressed).unwrap();
assert_eq!(key, from_wif);
}
#[test]
fn test_vector_key_one() {
let hex = "0000000000000000000000000000000000000000000000000000000000000001";
let key = PrivateKey::from_hex(hex).unwrap();
assert_eq!(key.to_hex(), hex.to_lowercase());
let mut expected_bytes = [0u8; 32];
expected_bytes[31] = 1;
assert_eq!(key.to_bytes(), expected_bytes);
let public_key = key.public_key();
let compressed = public_key.to_compressed();
assert_eq!(compressed.len(), 33);
assert!(compressed[0] == 0x02 || compressed[0] == 0x03);
}
#[test]
fn test_vector_pubkey_derivation() {
let hex = "0000000000000000000000000000000000000000000000000000000000000001";
let key = PrivateKey::from_hex(hex).unwrap();
let public_key = key.public_key();
let compressed_hex = public_key.to_hex(crate::public_key::PublicKeyFormat::Compressed);
assert_eq!(
compressed_hex.to_lowercase(),
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
);
}
#[test]
fn test_vector_wif_testnet() {
let key = PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000000001",
)
.unwrap();
let wif_mainnet = key.to_wif(Network::Mainnet);
let wif_testnet = key.to_wif(Network::Testnet);
assert!(
wif_mainnet.starts_with('K') || wif_mainnet.starts_with('L'),
"Mainnet WIF should start with K or L"
);
assert!(
wif_testnet.starts_with('c'),
"Testnet WIF should start with c"
);
let from_mainnet = PrivateKey::from_wif(&wif_mainnet).unwrap();
let from_testnet = PrivateKey::from_wif(&wif_testnet).unwrap();
assert_eq!(key, from_mainnet);
assert_eq!(key, from_testnet);
}
}