|
|
|
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
|
|
|
// All rights reserved.
|
|
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
|
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
|
|
// at your option. All files in the project carrying such
|
|
|
|
// notice may not be copied, modified, or distributed except
|
|
|
|
// according to those terms.
|
|
|
|
|
|
|
|
// #[macro_use]
|
|
|
|
// extern crate slice_as_array;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate p2p_net;
|
|
|
|
|
|
|
|
use p2p_net::log;
|
|
|
|
|
|
|
|
pub mod types;
|
|
|
|
|
|
|
|
pub mod bip39;
|
|
|
|
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
use crate::bip39::bip39_wordlist;
|
|
|
|
use crate::types::*;
|
|
|
|
use aes_gcm_siv::{
|
|
|
|
aead::{heapless::Vec as HeaplessVec, AeadInPlace, KeyInit},
|
|
|
|
Aes256GcmSiv, Nonce,
|
|
|
|
};
|
|
|
|
use argon2::{Algorithm, Argon2, AssociatedData, ParamsBuilder, Version};
|
|
|
|
use chacha20poly1305::XChaCha20Poly1305;
|
|
|
|
|
|
|
|
use image::{imageops::FilterType, io::Reader as ImageReader, ImageOutputFormat};
|
|
|
|
use safe_transmute::transmute_to_bytes;
|
|
|
|
|
|
|
|
use p2p_repo::types::{PubKey, Site, SiteType, Timestamp};
|
|
|
|
use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify};
|
|
|
|
use rand::prelude::*;
|
|
|
|
use serde_bare::{from_slice, to_vec};
|
|
|
|
|
|
|
|
pub fn enc_master_key(
|
|
|
|
master_key: [u8; 32],
|
|
|
|
key: [u8; 32],
|
|
|
|
nonce: u8,
|
|
|
|
wallet_id: WalletId,
|
|
|
|
) -> Result<[u8; 48], NgWalletError> {
|
|
|
|
let cipher = Aes256GcmSiv::new(&key.into());
|
|
|
|
let mut nonce_buffer = [0u8; 12];
|
|
|
|
nonce_buffer[0] = nonce;
|
|
|
|
let nonce = Nonce::from_slice(&nonce_buffer);
|
|
|
|
|
|
|
|
let mut buffer: HeaplessVec<u8, 48> = HeaplessVec::new(); // Note: buffer needs 16-bytes overhead for auth tag
|
|
|
|
buffer.extend_from_slice(&master_key);
|
|
|
|
|
|
|
|
// Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
|
|
|
|
cipher
|
|
|
|
.encrypt_in_place(nonce, &to_vec(&wallet_id).unwrap(), &mut buffer)
|
|
|
|
.map_err(|e| NgWalletError::EncryptionError)?;
|
|
|
|
|
|
|
|
// `buffer` now contains the encrypted master key
|
|
|
|
// println!("cipher {:?}", buffer);
|
|
|
|
Ok(buffer.into_array::<48>().unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dec_master_key(
|
|
|
|
ciphertext: [u8; 48],
|
|
|
|
key: [u8; 32],
|
|
|
|
nonce: u8,
|
|
|
|
wallet_id: WalletId,
|
|
|
|
) -> Result<[u8; 32], NgWalletError> {
|
|
|
|
let cipher = Aes256GcmSiv::new(&key.into());
|
|
|
|
let mut nonce_buffer = [0u8; 12];
|
|
|
|
nonce_buffer[0] = nonce;
|
|
|
|
let nonce = Nonce::from_slice(&nonce_buffer);
|
|
|
|
|
|
|
|
let mut buffer: HeaplessVec<u8, 48> = HeaplessVec::from_slice(&ciphertext).unwrap(); // Note: buffer needs 16-bytes overhead for auth tag
|
|
|
|
|
|
|
|
// Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
|
|
|
|
cipher
|
|
|
|
.decrypt_in_place(nonce, &to_vec(&wallet_id).unwrap(), &mut buffer)
|
|
|
|
.map_err(|e| NgWalletError::DecryptionError)?;
|
|
|
|
Ok(buffer.into_array::<32>().unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gen_nonce(peer_id: PubKey, nonce: u64) -> [u8; 24] {
|
|
|
|
let mut buffer = Vec::with_capacity(24);
|
|
|
|
buffer.extend_from_slice(&peer_id.slice()[0..16]);
|
|
|
|
buffer.extend_from_slice(&nonce.to_be_bytes());
|
|
|
|
buffer.try_into().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gen_associated_data(timestamp: Timestamp, wallet_id: WalletId) -> Vec<u8> {
|
|
|
|
let ser_wallet = to_vec(&wallet_id).unwrap();
|
|
|
|
[ser_wallet, timestamp.to_be_bytes().to_vec()].concat()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enc_encrypted_block(
|
|
|
|
block: &EncryptedWalletV0,
|
|
|
|
master_key: [u8; 32],
|
|
|
|
peer_id: PubKey,
|
|
|
|
nonce: u64,
|
|
|
|
timestamp: Timestamp,
|
|
|
|
wallet_id: WalletId,
|
|
|
|
) -> Result<Vec<u8>, NgWalletError> {
|
|
|
|
let ser_encrypted_block = to_vec(block).map_err(|e| NgWalletError::InternalError)?;
|
|
|
|
|
|
|
|
let nonce_buffer: [u8; 24] = gen_nonce(peer_id, nonce);
|
|
|
|
|
|
|
|
let cipher = XChaCha20Poly1305::new(&master_key.into());
|
|
|
|
|
|
|
|
let mut buffer: Vec<u8> = Vec::with_capacity(ser_encrypted_block.len() + 16); // Note: buffer needs 16-bytes overhead for auth tag
|
|
|
|
buffer.extend_from_slice(&ser_encrypted_block);
|
|
|
|
|
|
|
|
// Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
|
|
|
|
cipher
|
|
|
|
.encrypt_in_place(
|
|
|
|
&nonce_buffer.into(),
|
|
|
|
&gen_associated_data(timestamp, wallet_id),
|
|
|
|
&mut buffer,
|
|
|
|
)
|
|
|
|
.map_err(|e| NgWalletError::EncryptionError)?;
|
|
|
|
|
|
|
|
// `buffer` now contains the message ciphertext
|
|
|
|
// println!("encrypted_block ciphertext {:?}", buffer);
|
|
|
|
|
|
|
|
Ok(buffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dec_encrypted_block(
|
|
|
|
mut ciphertext: Vec<u8>,
|
|
|
|
master_key: [u8; 32],
|
|
|
|
peer_id: PubKey,
|
|
|
|
nonce: u64,
|
|
|
|
timestamp: Timestamp,
|
|
|
|
wallet_id: WalletId,
|
|
|
|
) -> Result<EncryptedWalletV0, NgWalletError> {
|
|
|
|
let nonce_buffer: [u8; 24] = gen_nonce(peer_id, nonce);
|
|
|
|
|
|
|
|
let cipher = XChaCha20Poly1305::new(&master_key.into());
|
|
|
|
|
|
|
|
// Decrypt `ciphertext` in-place, replacing its ciphertext context with the original plaintext
|
|
|
|
cipher
|
|
|
|
.decrypt_in_place(
|
|
|
|
&nonce_buffer.into(),
|
|
|
|
&gen_associated_data(timestamp, wallet_id),
|
|
|
|
&mut ciphertext,
|
|
|
|
)
|
|
|
|
.map_err(|e| NgWalletError::DecryptionError)?;
|
|
|
|
|
|
|
|
// `ciphertext` now contains the decrypted block
|
|
|
|
//println!("decrypted_block {:?}", ciphertext);
|
|
|
|
|
|
|
|
let decrypted_block =
|
|
|
|
from_slice::<EncryptedWalletV0>(&ciphertext).map_err(|e| NgWalletError::DecryptionError)?;
|
|
|
|
|
|
|
|
Ok(decrypted_block)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn derive_key_from_pass(pass: Vec<u8>, salt: [u8; 16], wallet_id: WalletId) -> [u8; 32] {
|
|
|
|
let params = ParamsBuilder::new()
|
|
|
|
.m_cost(50 * 1024)
|
|
|
|
.t_cost(2)
|
|
|
|
.p_cost(1)
|
|
|
|
.data(AssociatedData::new(wallet_id.slice()).unwrap())
|
|
|
|
.output_len(32)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
|
|
|
let mut out = [0u8; 32];
|
|
|
|
argon.hash_password_into(&pass, &salt, &mut out).unwrap();
|
|
|
|
out
|
|
|
|
}
|
|
|
|
use web_time::Instant;
|
|
|
|
|
|
|
|
pub fn open_wallet_with_pazzle(
|
|
|
|
wallet: Wallet,
|
|
|
|
pazzle: Vec<u8>,
|
|
|
|
pin: [u8; 4],
|
|
|
|
) -> Result<EncryptedWallet, NgWalletError> {
|
|
|
|
// each digit shouldnt be greater than 9
|
|
|
|
if pin[0] > 9 || pin[1] > 9 || pin[2] > 9 || pin[3] > 9 {
|
|
|
|
return Err(NgWalletError::InvalidPin);
|
|
|
|
}
|
|
|
|
|
|
|
|
let opening_pazzle = Instant::now();
|
|
|
|
|
|
|
|
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
|
|
|
|
.map_err(|e| NgWalletError::InvalidSignature)?;
|
|
|
|
|
|
|
|
match wallet {
|
|
|
|
Wallet::V0(v0) => {
|
|
|
|
let pazzle_key = derive_key_from_pass(
|
|
|
|
[pazzle, pin.to_vec()].concat(),
|
|
|
|
v0.content.salt_pazzle,
|
|
|
|
v0.id,
|
|
|
|
);
|
|
|
|
|
|
|
|
let master_key = dec_master_key(
|
|
|
|
v0.content.enc_master_key_pazzle,
|
|
|
|
pazzle_key,
|
|
|
|
v0.content.master_nonce,
|
|
|
|
v0.id,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
log!(
|
|
|
|
"opening of wallet with pazzle took: {} ms",
|
|
|
|
opening_pazzle.elapsed().as_millis()
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(EncryptedWallet::V0(dec_encrypted_block(
|
|
|
|
v0.content.encrypted,
|
|
|
|
master_key,
|
|
|
|
v0.content.peer_id,
|
|
|
|
v0.content.nonce,
|
|
|
|
v0.content.timestamp,
|
|
|
|
v0.id,
|
|
|
|
)?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn open_wallet_with_mnemonic(
|
|
|
|
wallet: Wallet,
|
|
|
|
mnemonic: [u16; 12],
|
|
|
|
pin: [u8; 4],
|
|
|
|
) -> Result<EncryptedWallet, NgWalletError> {
|
|
|
|
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
|
|
|
|
.map_err(|e| NgWalletError::InvalidSignature)?;
|
|
|
|
|
|
|
|
match wallet {
|
|
|
|
Wallet::V0(v0) => {
|
|
|
|
let mnemonic_key = derive_key_from_pass(
|
|
|
|
[transmute_to_bytes(&mnemonic), &pin].concat(),
|
|
|
|
v0.content.salt_mnemonic,
|
|
|
|
v0.id,
|
|
|
|
);
|
|
|
|
|
|
|
|
let master_key = dec_master_key(
|
|
|
|
v0.content.enc_master_key_mnemonic,
|
|
|
|
mnemonic_key,
|
|
|
|
v0.content.master_nonce,
|
|
|
|
v0.id,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(EncryptedWallet::V0(dec_encrypted_block(
|
|
|
|
v0.content.encrypted,
|
|
|
|
master_key,
|
|
|
|
v0.content.peer_id,
|
|
|
|
v0.content.nonce,
|
|
|
|
v0.content.timestamp,
|
|
|
|
v0.id,
|
|
|
|
)?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn display_mnemonic(mnemonic: &[u16; 12]) -> Vec<String> {
|
|
|
|
let res: Vec<String> = mnemonic
|
|
|
|
.into_iter()
|
|
|
|
.map(|i| String::from(bip39_wordlist[*i as usize]))
|
|
|
|
.collect();
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn display_pazzle(pazzle: Vec<u8>) -> Vec<String> {
|
|
|
|
// let res: Vec<String> = pazzle
|
|
|
|
// .into_iter()
|
|
|
|
// .map(|i| String::from(bip39_wordlist[*i as usize]))
|
|
|
|
// .collect();
|
|
|
|
// res
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> ShuffledPazzle {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let mut category_indices: Vec<u8> = (0..pazzle_length).collect();
|
|
|
|
//log!("{:?}", category_indices);
|
|
|
|
category_indices.shuffle(&mut rng);
|
|
|
|
//log!("{:?}", category_indices);
|
|
|
|
|
|
|
|
let mut emoji_indices: Vec<Vec<u8>> = Vec::with_capacity(pazzle_length.into());
|
|
|
|
for _ in 0..pazzle_length {
|
|
|
|
let mut idx: Vec<u8> = (0..15).collect();
|
|
|
|
//log!("{:?}", idx);
|
|
|
|
idx.shuffle(&mut rng);
|
|
|
|
//log!("{:?}", idx);
|
|
|
|
emoji_indices.push(idx)
|
|
|
|
}
|
|
|
|
ShuffledPazzle {
|
|
|
|
category_indices,
|
|
|
|
emoji_indices,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gen_shuffle_for_pin() -> Vec<u8> {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let mut digits: Vec<u8> = (0..10).collect();
|
|
|
|
//log!("{:?}", digits);
|
|
|
|
digits.shuffle(&mut rng);
|
|
|
|
//log!("{:?}", digits);
|
|
|
|
digits
|
|
|
|
}
|
|
|
|
|
|
|
|
/// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one)
|
|
|
|
/// and returns the Wallet, the pazzle and the mnemonic
|
|
|
|
pub async fn create_wallet_v0(
|
|
|
|
params: CreateWalletV0,
|
|
|
|
) -> Result<CreateWalletResultV0, NgWalletError> {
|
|
|
|
// TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values
|
|
|
|
|
|
|
|
let creating_pazzle = Instant::now();
|
|
|
|
|
|
|
|
// pazzle_length can only be 9, 12, or 15
|
|
|
|
if (params.pazzle_length != 9 && params.pazzle_length != 12 && params.pazzle_length != 15) {
|
|
|
|
return Err(NgWalletError::InvalidPazzleLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
// cannot submit wallet if we don't submit also the bootstrap
|
|
|
|
if params.send_bootstrap.is_none() && params.send_wallet {
|
|
|
|
return Err(NgWalletError::SubmissionError);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check validity of PIN
|
|
|
|
|
|
|
|
// shouldn't start with 0
|
|
|
|
if params.pin[0] == 0 {
|
|
|
|
return Err(NgWalletError::InvalidPin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// each digit shouldnt be greater than 9
|
|
|
|
if params.pin[0] > 9 || params.pin[1] > 9 || params.pin[2] > 9 || params.pin[3] > 9 {
|
|
|
|
return Err(NgWalletError::InvalidPin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for uniqueness of each digit
|
|
|
|
if params.pin[1] == params.pin[0]
|
|
|
|
|| params.pin[1] == params.pin[2]
|
|
|
|
|| params.pin[1] == params.pin[3]
|
|
|
|
|| params.pin[2] == params.pin[0]
|
|
|
|
|| params.pin[2] == params.pin[3]
|
|
|
|
|| params.pin[3] == params.pin[0]
|
|
|
|
{
|
|
|
|
return Err(NgWalletError::InvalidPin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for ascending series
|
|
|
|
if params.pin[1] == params.pin[0] + 1
|
|
|
|
&& params.pin[2] == params.pin[1] + 1
|
|
|
|
&& params.pin[3] == params.pin[2] + 1
|
|
|
|
{
|
|
|
|
return Err(NgWalletError::InvalidPin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for descending series
|
|
|
|
if params.pin[3] >= 3
|
|
|
|
&& params.pin[2] == params.pin[3] - 1
|
|
|
|
&& params.pin[1] == params.pin[2] - 1
|
|
|
|
&& params.pin[0] == params.pin[1] - 1
|
|
|
|
{
|
|
|
|
return Err(NgWalletError::InvalidPin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check validity of security text
|
|
|
|
let words: Vec<_> = params.security_txt.split_whitespace().collect();
|
|
|
|
let new_string = words.join(" ");
|
|
|
|
let count = new_string.chars().count();
|
|
|
|
if count < 10 || count > 100 {
|
|
|
|
return Err(NgWalletError::InvalidSecurityText);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check validity of image
|
|
|
|
let decoded_img = ImageReader::new(Cursor::new(params.security_img))
|
|
|
|
.with_guessed_format()
|
|
|
|
.map_err(|e| NgWalletError::InvalidSecurityImage)?
|
|
|
|
.decode()
|
|
|
|
.map_err(|e| NgWalletError::InvalidSecurityImage)?;
|
|
|
|
|
|
|
|
if decoded_img.height() < 150 || decoded_img.width() < 150 {
|
|
|
|
return Err(NgWalletError::InvalidSecurityImage);
|
|
|
|
}
|
|
|
|
|
|
|
|
let resized_img = decoded_img.resize_to_fill(400, 400, FilterType::Triangle);
|
|
|
|
|
|
|
|
let buffer: Vec<u8> = Vec::with_capacity(100000);
|
|
|
|
let mut cursor = Cursor::new(buffer);
|
|
|
|
resized_img
|
|
|
|
.write_to(&mut cursor, ImageOutputFormat::Jpeg(72))
|
|
|
|
.map_err(|e| NgWalletError::InvalidSecurityImage)?;
|
|
|
|
|
|
|
|
// creating the wallet keys
|
|
|
|
|
|
|
|
let (wallet_key, wallet_id) = generate_keypair();
|
|
|
|
|
|
|
|
let site = Site::create(SiteType::Individual).map_err(|e| NgWalletError::InternalError)?;
|
|
|
|
|
|
|
|
// let mut pazzle_random = vec![0u8; pazzle_length.into()];
|
|
|
|
// getrandom::getrandom(&mut pazzle_random).map_err(|e| NgWalletError::InternalError)?;
|
|
|
|
|
|
|
|
let mut ran = thread_rng();
|
|
|
|
|
|
|
|
let mut category_indices: Vec<u8> = (0..params.pazzle_length).collect();
|
|
|
|
category_indices.shuffle(&mut ran);
|
|
|
|
|
|
|
|
let mut pazzle = vec![0u8; params.pazzle_length.into()];
|
|
|
|
for (ix, i) in pazzle.iter_mut().enumerate() {
|
|
|
|
*i = ran.gen_range(0, 15) + (category_indices[ix] << 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
//println!("pazzle {:?}", pazzle);
|
|
|
|
|
|
|
|
let mut mnemonic = [0u16; 12];
|
|
|
|
for i in &mut mnemonic {
|
|
|
|
*i = ran.gen_range(0, 2048);
|
|
|
|
}
|
|
|
|
|
|
|
|
//println!("mnemonic {:?}", display_mnemonic(&mnemonic));
|
|
|
|
|
|
|
|
//slice_as_array!(&mnemonic, [String; 12])
|
|
|
|
//.ok_or(NgWalletError::InternalError)?
|
|
|
|
//.clone(),
|
|
|
|
|
|
|
|
let encrypted_block = EncryptedWalletV0 {
|
|
|
|
pazzle: pazzle.clone(),
|
|
|
|
mnemonic,
|
|
|
|
pin: params.pin,
|
|
|
|
sites: vec![site],
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut salt_pazzle = [0u8; 16];
|
|
|
|
getrandom::getrandom(&mut salt_pazzle).map_err(|e| NgWalletError::InternalError)?;
|
|
|
|
|
|
|
|
let mut salt_mnemonic = [0u8; 16];
|
|
|
|
getrandom::getrandom(&mut salt_mnemonic).map_err(|e| NgWalletError::InternalError)?;
|
|
|
|
|
|
|
|
//println!("salt_pazzle {:?}", salt_pazzle);
|
|
|
|
//println!("salt_mnemonic {:?}", salt_mnemonic);
|
|
|
|
|
|
|
|
let pazzle_key = derive_key_from_pass(
|
|
|
|
[pazzle.clone(), params.pin.to_vec()].concat(),
|
|
|
|
salt_pazzle,
|
|
|
|
wallet_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mnemonic_key = derive_key_from_pass(
|
|
|
|
[transmute_to_bytes(&mnemonic), ¶ms.pin].concat(),
|
|
|
|
salt_mnemonic,
|
|
|
|
wallet_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut master_key = [0u8; 32];
|
|
|
|
getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?;
|
|
|
|
|
|
|
|
let enc_master_key_pazzle = enc_master_key(master_key, pazzle_key, 0, wallet_id)?;
|
|
|
|
|
|
|
|
let enc_master_key_mnemonic = enc_master_key(master_key, mnemonic_key, 0, wallet_id)?;
|
|
|
|
|
|
|
|
let timestamp = now_timestamp();
|
|
|
|
|
|
|
|
let encrypted = enc_encrypted_block(
|
|
|
|
&encrypted_block,
|
|
|
|
master_key,
|
|
|
|
params.peer_id,
|
|
|
|
params.nonce,
|
|
|
|
timestamp,
|
|
|
|
wallet_id,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let wallet_content = WalletContentV0 {
|
|
|
|
security_img: cursor.into_inner(),
|
|
|
|
security_txt: new_string,
|
|
|
|
salt_pazzle,
|
|
|
|
salt_mnemonic,
|
|
|
|
enc_master_key_pazzle,
|
|
|
|
enc_master_key_mnemonic,
|
|
|
|
master_nonce: 0,
|
|
|
|
timestamp,
|
|
|
|
peer_id: params.peer_id,
|
|
|
|
nonce: params.nonce,
|
|
|
|
encrypted,
|
|
|
|
};
|
|
|
|
|
|
|
|
let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap();
|
|
|
|
|
|
|
|
let sig = sign(wallet_key, wallet_id, &ser_wallet).unwrap();
|
|
|
|
|
|
|
|
let wallet_v0 = WalletV0 {
|
|
|
|
/// ID
|
|
|
|
id: wallet_id,
|
|
|
|
/// Content
|
|
|
|
content: wallet_content,
|
|
|
|
/// Signature over content by wallet's private key
|
|
|
|
sig,
|
|
|
|
};
|
|
|
|
|
|
|
|
// let content = BootstrapContentV0 { servers: vec![] };
|
|
|
|
// let ser = serde_bare::to_vec(&content).unwrap();
|
|
|
|
// let sig = sign(wallet_key, wallet_id, &ser).unwrap();
|
|
|
|
|
|
|
|
// let bootstrap = Bootstrap::V0(BootstrapV0 {
|
|
|
|
// id: wallet_id,
|
|
|
|
// content,
|
|
|
|
// sig,
|
|
|
|
// });
|
|
|
|
|
|
|
|
// TODO send bootstrap (if)
|
|
|
|
// TODO send wallet (if)
|
|
|
|
|
|
|
|
log!(
|
|
|
|
"creating of wallet took: {} ms",
|
|
|
|
creating_pazzle.elapsed().as_millis()
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(CreateWalletResultV0 {
|
|
|
|
wallet: Wallet::V0(wallet_v0),
|
|
|
|
pazzle,
|
|
|
|
mnemonic,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use p2p_repo::utils::generate_keypair;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::BufReader;
|
|
|
|
use std::io::Read;
|
|
|
|
use std::io::Write;
|
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_gen_shuffle() {
|
|
|
|
let shuffle = gen_shuffle_for_pazzle_opening(9);
|
|
|
|
log!("{:?}", shuffle);
|
|
|
|
let shuffle = gen_shuffle_for_pazzle_opening(12);
|
|
|
|
log!("{:?}", shuffle);
|
|
|
|
let shuffle = gen_shuffle_for_pazzle_opening(15);
|
|
|
|
log!("{:?}", shuffle);
|
|
|
|
let digits = gen_shuffle_for_pin();
|
|
|
|
let digits = gen_shuffle_for_pin();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_std::test]
|
|
|
|
async fn create_wallet() {
|
|
|
|
// loading an image file from disk
|
|
|
|
let f = File::open("tests/valid_security_image.jpg")
|
|
|
|
.expect("open of tests/valid_security_image.jpg");
|
|
|
|
let mut reader = BufReader::new(f);
|
|
|
|
let mut img_buffer = Vec::new();
|
|
|
|
// Read file into vector.
|
|
|
|
reader
|
|
|
|
.read_to_end(&mut img_buffer)
|
|
|
|
.expect("read of valid_security_image.jpg");
|
|
|
|
|
|
|
|
let pin = [5, 2, 9, 1];
|
|
|
|
|
|
|
|
let creation = Instant::now();
|
|
|
|
|
|
|
|
let res = create_wallet_v0(CreateWalletV0::new(
|
|
|
|
img_buffer,
|
|
|
|
" know yourself ".to_string(),
|
|
|
|
pin,
|
|
|
|
9,
|
|
|
|
None,
|
|
|
|
false,
|
|
|
|
PubKey::Ed25519PubKey([
|
|
|
|
119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167, 107, 2, 113,
|
|
|
|
98, 243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86,
|
|
|
|
]),
|
|
|
|
0,
|
|
|
|
))
|
|
|
|
.await
|
|
|
|
.expect("create_wallet_v0");
|
|
|
|
|
|
|
|
log!(
|
|
|
|
"creation of wallet took: {} ms",
|
|
|
|
creation.elapsed().as_millis()
|
|
|
|
);
|
|
|
|
log!("-----------------------------");
|
|
|
|
|
|
|
|
let mut file = File::create("tests/wallet.ngw").expect("open wallet write file");
|
|
|
|
let ser_wallet = to_vec(&NgFile::V0(NgFileV0::Wallet(res.wallet.clone()))).unwrap();
|
|
|
|
file.write_all(&ser_wallet);
|
|
|
|
|
|
|
|
log!(
|
|
|
|
"wallet id: {:?}",
|
|
|
|
base64_url::encode(&res.wallet.id().slice())
|
|
|
|
);
|
|
|
|
log!("pazzle {:?}", res.pazzle);
|
|
|
|
log!("mnemonic {:?}", display_mnemonic(&res.mnemonic));
|
|
|
|
log!("pin {:?}", pin);
|
|
|
|
|
|
|
|
if let Wallet::V0(v0) = res.wallet {
|
|
|
|
log!("security text: {:?}", v0.content.security_txt);
|
|
|
|
|
|
|
|
let mut file =
|
|
|
|
File::create("tests/generated_security_image.jpg").expect("open write file");
|
|
|
|
file.write_all(&v0.content.security_img);
|
|
|
|
|
|
|
|
let f = File::open("tests/generated_security_image.jpg.compare")
|
|
|
|
.expect("open of generated_security_image.jpg.compare");
|
|
|
|
let mut reader = BufReader::new(f);
|
|
|
|
let mut generated_security_image_compare = Vec::new();
|
|
|
|
// Read file into vector.
|
|
|
|
reader
|
|
|
|
.read_to_end(&mut generated_security_image_compare)
|
|
|
|
.expect("read of generated_security_image.jpg.compare");
|
|
|
|
|
|
|
|
assert_eq!(v0.content.security_img, generated_security_image_compare);
|
|
|
|
|
|
|
|
let opening_mnemonic = Instant::now();
|
|
|
|
|
|
|
|
let w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), res.mnemonic, pin)
|
|
|
|
.expect("open with mnemonic");
|
|
|
|
//println!("encrypted part {:?}", w);
|
|
|
|
|
|
|
|
log!(
|
|
|
|
"opening of wallet with mnemonic took: {} ms",
|
|
|
|
opening_mnemonic.elapsed().as_millis()
|
|
|
|
);
|
|
|
|
let opening_pazzle = Instant::now();
|
|
|
|
|
|
|
|
let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle, pin)
|
|
|
|
.expect("open with pazzle");
|
|
|
|
//println!("encrypted part {:?}", w);
|
|
|
|
|
|
|
|
log!(
|
|
|
|
"opening of wallet with pazzle took: {} ms",
|
|
|
|
opening_pazzle.elapsed().as_millis()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|