bless
This commit is contained in:
parent
891f93de6b
commit
6dcf11c718
@ -1,5 +1,5 @@
|
|||||||
age-encryption.org/v1
|
age-encryption.org/v1
|
||||||
-> scrypt FP9Jzf9WMGgQj2HZPAbuDw 14
|
-> scrypt qz/wL52nh3MG/kb96jc2Mg 13
|
||||||
i+HlcYKckQXUKAtoY8SIjJUz15IE2GucgQM0sZxLx78
|
auC858eJBPN/QknA7lj7CdnrmKEm8EOSasOQpBkkJ/c
|
||||||
--- 5JVy3rsJpvCXTl41B6/k/aC0HqoAdyfH4I6efffwr/w
|
--- K1MwOdhmY33yGi7USChiazGeOj9uUGKCYvVOltWSA7A
|
||||||
-éŰÂ|Q÷ÚúÇ‘-D‰3´<%’Ą|~<7E>¨<07><>韥ť)ęŔÂ"ŠČ+ůńá(Ýţ°}<7D>ÂéëKüv”É<>'#<23><>°SťtUuîX,’ů|‡ ďŚÝZ>CËőĐ
šó•Ú:Y”#úÔ:Áť×Ô•Ţă LNMĽô“Ë‚ěŐÖ*-´¸čôăHnD<6E>˛ŁŃU«O›cÚoůJ×6ŠţŁ^ĺłc¸ňŽý«¦X<>0ŘĽp¸–K\’š—ę~âuX|Ě; |ßÎ7D¸MÄD‡;_źlő`’K†^Ž+ěAË
|
,-¨íÒ¦sf$LàoõÁzžìÇA3Á³ºvbÉ„¡r4Ü‘¶ùÀ!ÕHæDäâb÷±¤IBêó*<2A>5<EFBFBD>s<EFBFBD>ÚÓ
|
||||||
@ -15,6 +15,7 @@ mod tests {
|
|||||||
|
|
||||||
use std::io::{ Read, Write };
|
use std::io::{ Read, Write };
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn encrypt() {
|
fn encrypt() {
|
||||||
let passphrase = age::secrecy::SecretString::from("password");
|
let passphrase = age::secrecy::SecretString::from("password");
|
||||||
|
|
||||||
@ -25,10 +26,10 @@ mod tests {
|
|||||||
private_key: "thisisprivate".to_string()
|
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 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();
|
let mut stream = encryptor.wrap_output(file).unwrap();
|
||||||
stream.write_all(&b).unwrap();
|
stream.write_all(&b).unwrap();
|
||||||
stream.finish().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();
|
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);
|
dbg!(&wallet);
|
||||||
}
|
}
|
||||||
|
|||||||
2
wallet/Cargo.lock
generated
2
wallet/Cargo.lock
generated
@ -1693,8 +1693,10 @@ name = "wallet"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"age",
|
"age",
|
||||||
|
"bincode",
|
||||||
"hex",
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
|
"serde",
|
||||||
"sha3",
|
"sha3",
|
||||||
"shared",
|
"shared",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.16",
|
||||||
|
|||||||
@ -5,8 +5,10 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
age = { version = "0.11.1", features = ["cli-common"] }
|
age = { version = "0.11.1", features = ["cli-common"] }
|
||||||
|
bincode = { version = "2.0.1", features = ["serde"] }
|
||||||
hex = "0.4.3"
|
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"
|
sha3 = "0.10.8"
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
thiserror = "2.0.16"
|
thiserror = "2.0.16"
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use age::Identity;
|
||||||
|
use bincode::{Decode, Encode};
|
||||||
use k256::sha2::Digest;
|
use k256::sha2::Digest;
|
||||||
use k256::ecdsa::{
|
use k256::ecdsa::{
|
||||||
self,
|
self,
|
||||||
@ -5,13 +7,17 @@ use k256::ecdsa::{
|
|||||||
VerifyingKey,
|
VerifyingKey,
|
||||||
RecoveryId,
|
RecoveryId,
|
||||||
Signature,
|
Signature,
|
||||||
|
signature::Verifier,
|
||||||
};
|
};
|
||||||
use shared::core::{ Transaction, SignedTransaction, Address, };
|
use shared::core::{ Transaction, SignedTransaction, Address, };
|
||||||
use k256::elliptic_curve::rand_core::OsRng;
|
use k256::elliptic_curve::rand_core::OsRng;
|
||||||
use sha3::Keccak256;
|
use sha3::Keccak256;
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
|
static BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum WalletError {
|
pub enum WalletError {
|
||||||
#[error("No Private Key present in Wallet")]
|
#[error("No Private Key present in Wallet")]
|
||||||
@ -20,6 +26,12 @@ pub enum WalletError {
|
|||||||
#[error("Signature error: {0}")]
|
#[error("Signature error: {0}")]
|
||||||
SignatureError(#[from] ecdsa::Error),
|
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}")]
|
#[error("Provided Recovery ID is invalid: {0}")]
|
||||||
InvalidRecoveryId(u8),
|
InvalidRecoveryId(u8),
|
||||||
|
|
||||||
@ -27,26 +39,39 @@ pub enum WalletError {
|
|||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
|
|
||||||
#[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 {
|
pub struct Wallet {
|
||||||
address: Address,
|
address: Address,
|
||||||
balance: u64,
|
balance: u64,
|
||||||
nonce: u64,
|
nonce: u64,
|
||||||
private_key: Option<SigningKey>,
|
private_key: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
|
|
||||||
fn verify_signature(tx: &SignedTransaction) -> Result<VerifyingKey, WalletError>{
|
fn verify_self_signature(&self, sign_tx: &SignedTransaction) -> Result<(), WalletError>{
|
||||||
if let Some(rec_id) = RecoveryId::from_byte(tx.recovery_id()) {
|
if let Some(rec_id) = RecoveryId::from_byte(sign_tx.recovery_id()) {
|
||||||
let sig = Signature::from_slice(tx.signature())?;
|
let sig = Signature::from_slice(sign_tx.signature())?;
|
||||||
let hash = tx.tx().hash();
|
println!("Signature recovered");
|
||||||
Ok(VerifyingKey::recover_from_prehash(&hash, &sig, rec_id)?)
|
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 {
|
} else {
|
||||||
Err(WalletError::InvalidRecoveryId(tx.recovery_id()))
|
Err(WalletError::InvalidRecoveryId(sign_tx.recovery_id()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +79,8 @@ impl Wallet {
|
|||||||
SigningKey::random(&mut OsRng)
|
SigningKey::random(&mut OsRng)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address(&self) -> &[u8] {
|
pub fn address(&self) -> [u8; 20] {
|
||||||
&self.address
|
self.address.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nonce(&self) -> u64 {
|
pub fn nonce(&self) -> u64 {
|
||||||
@ -63,15 +88,15 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn public_key(&self) -> Result<VerifyingKey, WalletError> {
|
pub fn public_key(&self) -> Result<VerifyingKey, WalletError> {
|
||||||
if let Some(pk) = &self.private_key {
|
let pk = SigningKey::from_bytes(&self.private_key.into())?;
|
||||||
Ok(*pk.verifying_key())
|
Ok(*pk.verifying_key())
|
||||||
} else {
|
|
||||||
Err(WalletError::NoPrivateKeyProvided)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verifying_key_to_address(private_key: &SigningKey) -> Address {
|
pub fn private_key(&self) -> Result<SigningKey, WalletError> {
|
||||||
let public_key = private_key.verifying_key();
|
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.to_encoded_point(false);
|
||||||
let public_key_bytes = public_key_bytes.as_bytes();
|
let public_key_bytes = public_key_bytes.as_bytes();
|
||||||
|
|
||||||
@ -83,44 +108,54 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load(path: path::PathBuf) -> Result<Self, WalletError> {
|
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 {
|
fn new() -> Self {
|
||||||
let address = if let Some(pk) = &pk {
|
let pk = Wallet::generate_private_key();
|
||||||
Self::verifying_key_to_address(pk)
|
let address = Self::public_key_to_address(&pk.verifying_key());
|
||||||
} else {
|
|
||||||
Address::default()
|
|
||||||
};
|
|
||||||
Self {
|
Self {
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
balance: 0,
|
balance: 0,
|
||||||
address,
|
address,
|
||||||
private_key: pk
|
private_key: pk.to_bytes().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(&self, transaction: Transaction) -> Result<SignedTransaction, WalletError> {
|
pub fn sign(&self, transaction: Transaction) -> Result<SignedTransaction, WalletError> {
|
||||||
let hash = transaction.hash();
|
let hash = transaction.hash();
|
||||||
if let Some(pk) = &self.private_key {
|
let pk = SigningKey::from_bytes(&self.private_key.into())?;
|
||||||
let (signature, recovery_id) = pk.sign_prehash_recoverable(&hash)?;
|
let (signature, recovery_id) = pk.sign_prehash_recoverable(&hash)?;
|
||||||
Ok(SignedTransaction::new(
|
Ok(SignedTransaction::new(
|
||||||
transaction,
|
transaction,
|
||||||
signature.to_bytes().into(),
|
signature.to_bytes().into(),
|
||||||
recovery_id.into()
|
recovery_id.into()
|
||||||
))
|
))
|
||||||
} else {
|
|
||||||
Err(WalletError::NoPrivateKeyProvided)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn acc_new_sign_no_key() {
|
fn acc_new_sign_no_key() {
|
||||||
let wallet = Wallet::new(None);
|
let wallet = Wallet::new();
|
||||||
let to_address: [u8; 20] = [1u8; 20];
|
let to_address: [u8; 20] = [1u8; 20];
|
||||||
let mut wallet_addr = [0u8; 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(
|
let tx = Transaction::new(
|
||||||
wallet_addr,
|
wallet_addr,
|
||||||
to_address,
|
to_address,
|
||||||
@ -132,30 +167,64 @@ fn acc_new_sign_no_key() {
|
|||||||
assert!(matches!(ret, Err(WalletError::NoPrivateKeyProvided)))
|
assert!(matches!(ret, Err(WalletError::NoPrivateKeyProvided)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
const WALLET_PATH: &str = "./wallet.age";
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use age::secrecy::SecretString;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
mod new {
|
||||||
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn acc_new_sign_with_key() -> Result<(), WalletError> {
|
fn wallet_new() -> Result<(), WalletError> {
|
||||||
let pk = Wallet::generate_private_key();
|
let wallet = Wallet::new();
|
||||||
let wallet = Wallet::new(Some(pk));
|
let passphrase = SecretString::from("password");
|
||||||
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)?;
|
|
||||||
|
|
||||||
let orig_pub_key_bytes = orig_public_key.to_encoded_point(false);
|
let bin_wallet = bincode::encode_to_vec::<Wallet, _>(wallet, BINCODE_CONFIG)?;
|
||||||
let new_pub_key_bytes = pub_key.to_encoded_point(false);
|
|
||||||
|
|
||||||
assert_eq!(orig_pub_key_bytes, new_pub_key_bytes);
|
|
||||||
|
|
||||||
|
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(())
|
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
BIN
wallet/wallet.age
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user