fix(desktop-wallet): update crypto APIs for bech32 v0.11 and bip39 v2
- Update bech32 encoding to use Hrp struct and segwit::encode - Update bip39 to use from_entropy() instead of generate_in() - Update Mnemonic::parse() instead of parse_in(Language) - Fix HMAC ambiguity with explicit type annotation - Add Debug and Clone derives to EncryptedWallet - Use show_menu_on_left_click() (deprecation fix) - Add placeholder icons for development builds
This commit is contained in:
parent
ce5c996b35
commit
f23e7928ea
8 changed files with 27 additions and 37 deletions
BIN
apps/desktop-wallet/src-tauri/icons/128x128.png
Normal file
BIN
apps/desktop-wallet/src-tauri/icons/128x128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 B |
BIN
apps/desktop-wallet/src-tauri/icons/128x128@2x.png
Normal file
BIN
apps/desktop-wallet/src-tauri/icons/128x128@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 B |
BIN
apps/desktop-wallet/src-tauri/icons/32x32.png
Normal file
BIN
apps/desktop-wallet/src-tauri/icons/32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 B |
0
apps/desktop-wallet/src-tauri/icons/icon.icns
Normal file
0
apps/desktop-wallet/src-tauri/icons/icon.icns
Normal file
0
apps/desktop-wallet/src-tauri/icons/icon.ico
Normal file
0
apps/desktop-wallet/src-tauri/icons/icon.ico
Normal file
BIN
apps/desktop-wallet/src-tauri/icons/icon.png
Normal file
BIN
apps/desktop-wallet/src-tauri/icons/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 B |
|
|
@ -5,26 +5,29 @@
|
|||
//! - Argon2id password-based key derivation
|
||||
//! - ChaCha20-Poly1305 authenticated encryption
|
||||
//! - Ed25519 key derivation from seed
|
||||
//! - Bech32 address encoding
|
||||
//! - Bech32m address encoding
|
||||
|
||||
use argon2::{
|
||||
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
||||
password_hash::SaltString,
|
||||
Argon2, Params,
|
||||
};
|
||||
use bip39::{Language, Mnemonic};
|
||||
use bip39::Mnemonic;
|
||||
use chacha20poly1305::{
|
||||
aead::{Aead, KeyInit},
|
||||
ChaCha20Poly1305, Nonce,
|
||||
};
|
||||
use hmac::{Hmac, Mac};
|
||||
use rand::RngCore;
|
||||
use hmac::Mac;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use sha2::Sha512;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// Type alias for HMAC-SHA512 to avoid ambiguity
|
||||
type HmacSha512 = hmac::Hmac<Sha512>;
|
||||
|
||||
/// Encrypted wallet data stored on disk
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EncryptedWallet {
|
||||
/// Argon2 salt (22 bytes, base64 encoded)
|
||||
pub salt: String,
|
||||
|
|
@ -46,7 +49,10 @@ pub struct SeedData {
|
|||
/// Generate a new random 24-word BIP39 mnemonic
|
||||
pub fn generate_mnemonic() -> Result<String> {
|
||||
// Generate 256 bits of entropy for 24 words
|
||||
let mnemonic = Mnemonic::generate_in(Language::English, 24)
|
||||
let mut entropy = [0u8; 32]; // 256 bits for 24 words
|
||||
OsRng.fill_bytes(&mut entropy);
|
||||
|
||||
let mnemonic = Mnemonic::from_entropy(&entropy)
|
||||
.map_err(|e| Error::Crypto(format!("Failed to generate mnemonic: {}", e)))?;
|
||||
|
||||
Ok(mnemonic.to_string())
|
||||
|
|
@ -54,15 +60,15 @@ pub fn generate_mnemonic() -> Result<String> {
|
|||
|
||||
/// Validate a BIP39 mnemonic phrase
|
||||
pub fn validate_mnemonic(phrase: &str) -> Result<()> {
|
||||
Mnemonic::parse_in(Language::English, phrase)
|
||||
.map_err(|e| Error::InvalidMnemonic)?;
|
||||
Mnemonic::parse(phrase)
|
||||
.map_err(|_| Error::InvalidMnemonic)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Derive a 64-byte seed from mnemonic using BIP39
|
||||
/// The passphrase is optional (empty string if not used)
|
||||
pub fn mnemonic_to_seed(mnemonic: &str, passphrase: &str) -> Result<SeedData> {
|
||||
let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)
|
||||
let mnemonic = Mnemonic::parse(mnemonic)
|
||||
.map_err(|_| Error::InvalidMnemonic)?;
|
||||
|
||||
let seed = mnemonic.to_seed(passphrase);
|
||||
|
|
@ -169,14 +175,13 @@ pub fn decrypt_seed(wallet: &EncryptedWallet, password: &str) -> Result<SeedData
|
|||
pub fn derive_ed25519_keypair(seed: &[u8; 64], index: u32) -> ([u8; 32], [u8; 32]) {
|
||||
// Use HMAC-SHA512 for deterministic key derivation
|
||||
// Path: m/44'/synor'/0'/0/index
|
||||
type HmacSha512 = Hmac<Sha512>;
|
||||
|
||||
let mut mac = HmacSha512::new_from_slice(b"ed25519 seed").unwrap();
|
||||
let mut mac: HmacSha512 = Mac::new_from_slice(b"ed25519 seed").unwrap();
|
||||
mac.update(seed);
|
||||
let master = mac.finalize().into_bytes();
|
||||
|
||||
// Derive child key for index
|
||||
let mut mac = HmacSha512::new_from_slice(&master[32..]).unwrap();
|
||||
let mut mac: HmacSha512 = Mac::new_from_slice(&master[32..]).unwrap();
|
||||
mac.update(&master[..32]);
|
||||
mac.update(&index.to_be_bytes());
|
||||
let derived = mac.finalize().into_bytes();
|
||||
|
|
@ -193,36 +198,21 @@ pub fn derive_ed25519_keypair(seed: &[u8; 64], index: u32) -> ([u8; 32], [u8; 32
|
|||
}
|
||||
|
||||
/// Generate a Synor address from a public key
|
||||
/// Format: synor1<bech32-encoded-pubkey-hash>
|
||||
/// Format: synor1<bech32m-encoded-pubkey-hash>
|
||||
pub fn pubkey_to_address(pubkey: &[u8; 32], testnet: bool) -> Result<String> {
|
||||
use bech32::Hrp;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
// Hash the public key (SHA256 then take first 20 bytes like Bitcoin)
|
||||
let hash = Sha256::digest(pubkey);
|
||||
let pubkey_hash: Vec<u8> = hash[..20].to_vec();
|
||||
let pubkey_hash: &[u8] = &hash[..20];
|
||||
|
||||
// Convert to 5-bit groups for bech32
|
||||
let mut data5 = Vec::with_capacity(33);
|
||||
data5.push(bech32::u5::try_from_u8(0).unwrap()); // version byte
|
||||
let hrp_str = if testnet { "tsynor" } else { "synor" };
|
||||
let hrp = Hrp::parse(hrp_str)
|
||||
.map_err(|e| Error::Crypto(format!("Invalid HRP: {}", e)))?;
|
||||
|
||||
// Convert 8-bit to 5-bit
|
||||
let mut acc = 0u32;
|
||||
let mut bits = 0u32;
|
||||
for byte in &pubkey_hash {
|
||||
acc = (acc << 8) | (*byte as u32);
|
||||
bits += 8;
|
||||
while bits >= 5 {
|
||||
bits -= 5;
|
||||
data5.push(bech32::u5::try_from_u8(((acc >> bits) & 31) as u8).unwrap());
|
||||
}
|
||||
}
|
||||
if bits > 0 {
|
||||
data5.push(bech32::u5::try_from_u8(((acc << (5 - bits)) & 31) as u8).unwrap());
|
||||
}
|
||||
|
||||
let hrp = if testnet { "tsynor" } else { "synor" };
|
||||
|
||||
bech32::encode(hrp, data5, bech32::Variant::Bech32)
|
||||
// Encode using bech32 segwit encoding (version 0)
|
||||
bech32::segwit::encode(hrp, bech32::segwit::VERSION_0, pubkey_hash)
|
||||
.map_err(|e| Error::Crypto(format!("Bech32 encoding failed: {}", e)))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ pub fn run() {
|
|||
let _tray = TrayIconBuilder::new()
|
||||
.icon(app.default_window_icon().unwrap().clone())
|
||||
.menu(&menu)
|
||||
.menu_on_left_click(false)
|
||||
.show_menu_on_left_click(false)
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
handle_tray_event(tray.app_handle(), event);
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue