This commit is contained in:
victor 2025-09-06 00:04:29 +02:00
parent 891f93de6b
commit 6dcf11c718
6 changed files with 143 additions and 69 deletions

View File

@ -1,5 +1,5 @@
age-encryption.org/v1
-> scrypt FP9Jzf9WMGgQj2HZPAbuDw 14
i+HlcYKckQXUKAtoY8SIjJUz15IE2GucgQM0sZxLx78
--- 5JVy3rsJpvCXTl41B6/k/aC0HqoAdyfH4I6efffwr/w
-éŰÂ|Q÷ÚúÇ‘-D‰3´<%’Ą|~<7E>¨<07><>韥ť)ęŔÂ"ŠČ+ůńá(Ýţ°}<7D>ÂéëKüv”É<>'#<23><>°SťtUuîX,’ů|‡ ďŚÝZ>CËőĐ šó•Ú:Y”#úÔ:Áť×Ô•Ţă LNMĽô“ËěŐ Ö*-´¸ čôăHnD<6E>˛ŁŃU«OcÚoůJ×ţŁ^ĺłc¸ňŽý«¦X<>0ŘĽp¸K\’š—ę~âuX|Ě; |ßÎ7 D¸MÄD‡;_źlő`K†^Ž+ěAË
-> scrypt qz/wL52nh3MG/kb96jc2Mg 13
auC858eJBPN/QknA7lj7CdnrmKEm8EOSasOQpBkkJ/c
--- K1MwOdhmY33yGi7USChiazGeOj9uUGKCYvVOltWSA7A
,-¨íÒ¦sf$LàoõÁzžìÇ A3Á³ºvbÉ„¡ r4ܶùÀ!ÕHæDäâb÷±¤IBêó*<2A>5<EFBFBD>s<EFBFBD>ÚÓ

View File

@ -15,6 +15,7 @@ mod tests {
use std::io::{ Read, Write };
#[test]
fn encrypt() {
let passphrase = age::secrecy::SecretString::from("password");
@ -25,10 +26,10 @@ mod tests {
private_key: "thisisprivate".to_string()
};
let b = bincode::serde::encode_to_vec::<Wallet, bincode::config::Configuration>(wallet, bincode::config::Configuration::default()).unwrap();
let b = bincode::serde::encode_to_vec::<Wallet, bincode::config::Configuration>(wallet, bincode::config::standard()).unwrap();
let encryptor = age::Encryptor::with_user_passphrase(passphrase);
let file = std::fs::OpenOptions::new().create(true).write(true).open(OUT_FILE).unwrap();
let file = std::fs::OpenOptions::new().create(true).truncate(true).write(true).open(OUT_FILE).unwrap();
let mut stream = encryptor.wrap_output(file).unwrap();
stream.write_all(&b).unwrap();
stream.finish().unwrap();
@ -47,7 +48,7 @@ mod tests {
decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity)).unwrap().read_to_end(&mut buf).unwrap();
let (wallet, _): (Wallet, usize) = bincode::serde::decode_from_slice::<Wallet, bincode::config::Configuration>(&buf, bincode::config::Configuration::default()).unwrap();
let (wallet, _): (Wallet, usize) = bincode::serde::decode_from_slice::<Wallet, bincode::config::Configuration>(&buf, bincode::config::standard()).unwrap();
dbg!(&wallet);
}

2
wallet/Cargo.lock generated
View File

@ -1693,8 +1693,10 @@ name = "wallet"
version = "0.1.0"
dependencies = [
"age",
"bincode",
"hex",
"k256",
"serde",
"sha3",
"shared",
"thiserror 2.0.16",

View File

@ -5,8 +5,10 @@ edition = "2024"
[dependencies]
age = { version = "0.11.1", features = ["cli-common"] }
bincode = { version = "2.0.1", features = ["serde"] }
hex = "0.4.3"
k256 = { version = "0.13.4", features = ["serde"] }
k256 = { version = "0.13.4", features = ["ecdsa-core", "serde"] }
serde = { version = "1.0.219", features = ["derive"] }
sha3 = "0.10.8"
shared = { path = "../shared" }
thiserror = "2.0.16"

View File

@ -1,3 +1,5 @@
use age::Identity;
use bincode::{Decode, Encode};
use k256::sha2::Digest;
use k256::ecdsa::{
self,
@ -5,13 +7,17 @@ use k256::ecdsa::{
VerifyingKey,
RecoveryId,
Signature,
signature::Verifier,
};
use shared::core::{ Transaction, SignedTransaction, Address, };
use k256::elliptic_curve::rand_core::OsRng;
use sha3::Keccak256;
use std::io::Read;
use std::path;
static BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
#[derive(Debug, thiserror::Error)]
pub enum WalletError {
#[error("No Private Key present in Wallet")]
@ -20,6 +26,12 @@ pub enum WalletError {
#[error("Signature error: {0}")]
SignatureError(#[from] ecdsa::Error),
#[error("Encryption Error: {0}")]
EncryptionError(#[from] age::EncryptError),
#[error("Decryption Error: {0}")]
DecryptionError(#[from] age::DecryptError),
#[error("Provided Recovery ID is invalid: {0}")]
InvalidRecoveryId(u8),
@ -27,26 +39,39 @@ pub enum WalletError {
IO(#[from] std::io::Error),
#[error("")]
InvalidHashLength
InvalidHashLength,
#[error("Passphrase Error: {0}")]
PassphraseError(String),
#[error("Decode Error: {0}")]
DecodeError(#[from] bincode::error::DecodeError),
#[error("Encode Error: {0}")]
EncodeError(#[from] bincode::error::EncodeError),
}
#[derive(Debug)]
#[derive(Debug, Encode, Decode)]
pub struct Wallet {
address: Address,
balance: u64,
nonce: u64,
private_key: Option<SigningKey>,
private_key: [u8; 32],
}
impl Wallet {
fn verify_signature(tx: &SignedTransaction) -> Result<VerifyingKey, WalletError>{
if let Some(rec_id) = RecoveryId::from_byte(tx.recovery_id()) {
let sig = Signature::from_slice(tx.signature())?;
let hash = tx.tx().hash();
Ok(VerifyingKey::recover_from_prehash(&hash, &sig, rec_id)?)
fn verify_self_signature(&self, sign_tx: &SignedTransaction) -> Result<(), WalletError>{
if let Some(rec_id) = RecoveryId::from_byte(sign_tx.recovery_id()) {
let sig = Signature::from_slice(sign_tx.signature())?;
println!("Signature recovered");
let hash = sign_tx.tx().hash();
let pub_key = VerifyingKey::recover_from_prehash(&hash, &sig, rec_id).unwrap();
// let pub_key = self.public_key()?;
println!("pubkey recovered");
Ok(pub_key.verify(&hash, &sig).unwrap())
} else {
Err(WalletError::InvalidRecoveryId(tx.recovery_id()))
Err(WalletError::InvalidRecoveryId(sign_tx.recovery_id()))
}
}
@ -54,8 +79,8 @@ impl Wallet {
SigningKey::random(&mut OsRng)
}
pub fn address(&self) -> &[u8] {
&self.address
pub fn address(&self) -> [u8; 20] {
self.address.clone()
}
pub fn nonce(&self) -> u64 {
@ -63,15 +88,15 @@ impl Wallet {
}
pub fn public_key(&self) -> Result<VerifyingKey, WalletError> {
if let Some(pk) = &self.private_key {
Ok(*pk.verifying_key())
} else {
Err(WalletError::NoPrivateKeyProvided)
}
let pk = SigningKey::from_bytes(&self.private_key.into())?;
Ok(*pk.verifying_key())
}
pub fn verifying_key_to_address(private_key: &SigningKey) -> Address {
let public_key = private_key.verifying_key();
pub fn private_key(&self) -> Result<SigningKey, WalletError> {
Ok(SigningKey::from_bytes(&self.private_key.into())?)
}
pub fn public_key_to_address(public_key: &VerifyingKey) -> Address {
let public_key_bytes = public_key.to_encoded_point(false);
let public_key_bytes = public_key_bytes.as_bytes();
@ -83,44 +108,54 @@ impl Wallet {
}
fn load(path: path::PathBuf) -> Result<Self, WalletError> {
let content = std::fs::read(path)?;
let file = std::fs::OpenOptions::new().read(true).open(path)?;
let decryptor = age::Decryptor::new(file)?;
let passphrase = match age::cli_common::read_secret(
"Unlock your Wallet",
"Please enter the passphrase",
Some("confirm?")
) {
Ok(p) => p,
Err(e) => return Err(WalletError::PassphraseError(format!("{e}")))
};
let mut buf = Vec::new();
let identity = age::scrypt::Identity::new(passphrase);
let mut reader = decryptor.decrypt(std::iter::once(&identity as &dyn Identity))?;
reader.read_to_end(&mut buf)?;
let wallet: Wallet = bincode::decode_from_slice(&buf, BINCODE_CONFIG)?.0;
Ok(wallet)
}
fn new(pk: Option<SigningKey>) -> Self {
let address = if let Some(pk) = &pk {
Self::verifying_key_to_address(pk)
} else {
Address::default()
};
fn new() -> Self {
let pk = Wallet::generate_private_key();
let address = Self::public_key_to_address(&pk.verifying_key());
Self {
nonce: 0,
balance: 0,
address,
private_key: pk
private_key: pk.to_bytes().into()
}
}
pub fn sign(&self, transaction: Transaction) -> Result<SignedTransaction, WalletError> {
let hash = transaction.hash();
if let Some(pk) = &self.private_key {
let (signature, recovery_id) = pk.sign_prehash_recoverable(&hash)?;
Ok(SignedTransaction::new(
transaction,
signature.to_bytes().into(),
recovery_id.into()
))
} else {
Err(WalletError::NoPrivateKeyProvided)
}
let pk = SigningKey::from_bytes(&self.private_key.into())?;
let (signature, recovery_id) = pk.sign_prehash_recoverable(&hash)?;
Ok(SignedTransaction::new(
transaction,
signature.to_bytes().into(),
recovery_id.into()
))
}
}
#[test]
fn acc_new_sign_no_key() {
let wallet = Wallet::new(None);
let wallet = Wallet::new();
let to_address: [u8; 20] = [1u8; 20];
let mut wallet_addr = [0u8; 20];
wallet_addr.copy_from_slice(wallet.address());
wallet_addr.copy_from_slice(&wallet.address());
let tx = Transaction::new(
wallet_addr,
to_address,
@ -132,30 +167,64 @@ fn acc_new_sign_no_key() {
assert!(matches!(ret, Err(WalletError::NoPrivateKeyProvided)))
}
#[test]
fn acc_new_sign_with_key() -> Result<(), WalletError> {
let pk = Wallet::generate_private_key();
let wallet = Wallet::new(Some(pk));
let orig_public_key = wallet.public_key()?;
let mut wallet_addr = [0u8; 20];
let to_address = [1u8; 20];
wallet_addr.copy_from_slice(wallet.address());
let tx = Transaction::new(
wallet_addr,
to_address,
500,
wallet.nonce(),
format!("")
);
let ret = wallet.sign(tx);
assert!(matches!(ret, Ok(SignedTransaction{..})));
let msg = ret.unwrap();
let pub_key = Wallet::verify_signature(&msg)?;
#[cfg(test)]
mod test {
const WALLET_PATH: &str = "./wallet.age";
let orig_pub_key_bytes = orig_public_key.to_encoded_point(false);
let new_pub_key_bytes = pub_key.to_encoded_point(false);
use std::io::Write;
assert_eq!(orig_pub_key_bytes, new_pub_key_bytes);
use age::secrecy::SecretString;
Ok(())
use super::*;
mod new {
use super::*;
#[test]
fn wallet_new() -> Result<(), WalletError> {
let wallet = Wallet::new();
let passphrase = SecretString::from("password");
let bin_wallet = bincode::encode_to_vec::<Wallet, _>(wallet, BINCODE_CONFIG)?;
let file = std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(WALLET_PATH)?;
let encryptor = age::Encryptor::with_user_passphrase(passphrase);
let mut writer = encryptor.wrap_output(file)?;
writer.write_all(&bin_wallet)?;
writer.finish()?;
Ok(())
}
}
mod load {
use super::*;
#[test]
fn wallet_load() -> Result<(), WalletError> {
let wallet = Wallet::load(WALLET_PATH.into())?;
Ok(())
}
}
mod sign {
use super::*;
#[test]
fn load_sign_verify() -> Result<(), WalletError> {
let wallet = Wallet::load(WALLET_PATH.into())?;
let tx = Transaction::new(wallet.address(), Default::default(), 500, wallet.nonce() + 1, "This is my data".to_string());
// let mut hash = Keccak256::new();
// hash.update(tx.data());
let hash = tx.hash();
let pk: ecdsa::SigningKey = wallet.private_key()?;
let (signature, recovery_id) = pk.sign_recoverable(&hash)?;
// let sig_tx = SignedTransaction::new(tx, signature.to_bytes().into(), recovery_id.into());
let vk = pk.verifying_key();
vk.verify(&hash, &signature).unwrap();
// wallet.verify_self_signature(&sig_tx)?;
Ok(())
}
}
}

BIN
wallet/wallet.age Normal file

Binary file not shown.