// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers // All rights reserved. // Licensed under the Apache License, Version 2.0 // // or the MIT license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use std::{collections::HashMap, fmt}; use web_time::SystemTime; use zeroize::{Zeroize, ZeroizeOnDrop}; use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; use p2p_net::types::*; use p2p_repo::types::*; /// WalletId is a PubKey pub type WalletId = PubKey; /// BootstrapId is a WalletId pub type BootstrapId = WalletId; /// Bootstrap Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BootstrapV0 { /// ID pub id: BootstrapId, /// Content pub content: BootstrapContentV0, /// Signature over content by wallet's private key pub sig: Sig, } /// Bootstrap info #[derive(Clone, Debug, Serialize, Deserialize)] pub enum Bootstrap { V0(BootstrapV0), } impl Bootstrap { pub fn id(&self) -> BootstrapId { match self { Bootstrap::V0(v0) => v0.id, } } pub fn content_as_bytes(&self) -> Vec { match self { Bootstrap::V0(v0) => serde_bare::to_vec(&v0.content).unwrap(), } } pub fn sig(&self) -> Sig { match self { Bootstrap::V0(v0) => v0.sig, } } } /// Device info Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ClientV0 { pub priv_key: PrivKey, pub storage_master_key: SymKey, /// list of users that should be opened automatically (at launch, after wallet opened) on this device pub auto_open: Vec, } /// EncryptedWallet block Version 0 #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] pub struct EncryptedWalletV0 { pub wallet_privkey: PrivKey, #[serde(with = "serde_bytes")] pub pazzle: Vec, pub mnemonic: [u16; 12], pub pin: [u8; 4], // first in the list is the main Site (Personal) #[zeroize(skip)] pub sites: Vec, // list of brokers and their connection details #[zeroize(skip)] pub brokers: Vec, // list of all devices of the user #[zeroize(skip)] pub clients: Vec, #[zeroize(skip)] pub overlay_core_overrides: HashMap>, /// third parties data saved in the wallet. the string (key) in the hashmap should be unique among vendors. /// the format of the byte array (value) is up to the vendor, to serde as needed. #[zeroize(skip)] pub third_parties: HashMap>, } /// EncryptedWallet block #[derive(Clone, Debug, Serialize, Deserialize)] pub enum EncryptedWallet { V0(EncryptedWalletV0), } /// Wallet content Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WalletContentV0 { #[serde(with = "serde_bytes")] pub security_img: Vec, pub security_txt: String, /// can be 9, 12 or 15 (or 0, in this case salt_pazzle and enc_master_key_pazzle are filled with zeros and should not be used) pub pazzle_length: u8, pub salt_pazzle: [u8; 16], pub salt_mnemonic: [u8; 16], // encrypted master keys. first is encrypted with pazzle, second is encrypted with mnemonic // AD = wallet_id #[serde(with = "BigArray")] pub enc_master_key_pazzle: [u8; 48], #[serde(with = "BigArray")] pub enc_master_key_mnemonic: [u8; 48], // nonce for the encryption of masterkey // incremented only if the masterkey changes // be very careful with incrementing this, as a conflict would result in total loss of crypto guarantees. pub master_nonce: u8, pub timestamp: Timestamp, // the peerId that updated this version of the Wallet. this value is truncated by half and concatenated with the nonce pub peer_id: PubKey, pub nonce: u64, // WalletLog0 content encrypted with XChaCha20Poly1305, AD = timestamp and walletID #[serde(with = "serde_bytes")] pub encrypted: Vec, } /// Wallet Log #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WalletLog0 { pub log: Vec<(SystemTime, WalletOperationV0)>, } /// WalletOperation #[derive(Clone, Debug, Serialize, Deserialize)] pub enum WalletOperationV0 { CreateWalletV0(WalletOpCreateV0), AddSiteV0(SiteV0), RemoveSiteV0(Identity), AddBrokerV0(BrokerInfoV0), RemoveBrokerV0(BrokerInfoV0), AddClientV0(ClientV0), AddOverlayCoreOverrideV0((OverlayId, Vec)), RemoveOverlayCoreOverrideV0(OverlayId), AddSiteCoreV0((Identity, PubKey)), RemoveSiteCoreV0((Identity, PubKey)), AddSiteBootstrapV0((Identity, PubKey)), RemoveSiteBootstrapV0((Identity, PubKey)), AddThirdPartyDataV0((String, Vec)), RemoveThirdPartyDataV0(String), SetSiteRBDRefV0((Identity, ObjectRef)), SetSiteRepoSecretV0((Identity, SymKey)), } /// WalletOp Create V0 /// first operation in the log /// also serialized and encoded in Rescue QRcode #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] pub struct WalletOpCreateV0 { pub wallet_privkey: PrivKey, #[serde(with = "serde_bytes")] pub pazzle: Vec, pub mnemonic: [u16; 12], pub pin: [u8; 4], #[zeroize(skip)] pub personal_site: SiteV0, // list of brokers and their connection details #[zeroize(skip)] pub brokers: Vec, #[zeroize(skip)] pub client: ClientV0, } /// Reduced Wallet content Version 0, for Login QRcode #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ReducedWalletContentV0 { /// can be 9, 12 or 15 (or 0, in this case salt_pazzle and enc_master_key_pazzle are filled with zeros and should not be used) pub pazzle_length: u8, pub salt_pazzle: [u8; 16], pub salt_mnemonic: [u8; 16], // encrypted master keys. first is encrypted with pazzle, second is encrypted with mnemonic // AD = wallet_id #[serde(with = "BigArray")] pub enc_master_key_pazzle: [u8; 48], #[serde(with = "BigArray")] pub enc_master_key_mnemonic: [u8; 48], // nonce for the encryption of masterkey // incremented only if the masterkey changes // be very careful with incrementing this, as a conflict would result in total loss of crypto guarantees. pub master_nonce: u8, pub timestamp: Timestamp, // the peerId that updated this version of the Wallet. this value is truncated by half and concatenated with the nonce pub peer_id: PubKey, pub nonce: u64, // ReducedEncryptedWalletV0 content encrypted with XChaCha20Poly1305, AD = timestamp and walletID #[serde(with = "serde_bytes")] pub encrypted: Vec, } /// Broker Info Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub enum BrokerInfoV0 { ServerV0(BrokerServerV0), CoreV0(BrokerCoreV0), } /// ReducedEncryptedWallet block Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ReducedEncryptedWalletV0 { // main Site (Personal) pub personal_site: ReducedSiteV0, // list of brokers and their connection details pub brokers: Vec, pub client: ClientV0, } /// ReducedEncryptedWallet block #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ReducedEncryptedWallet { V0(ReducedEncryptedWalletV0), } /// Wallet Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WalletV0 { /// ID pub id: WalletId, /// Content pub content: WalletContentV0, /// Signature over content by wallet's private key pub sig: Sig, } /// Wallet info #[derive(Clone, Debug, Serialize, Deserialize)] pub enum Wallet { V0(WalletV0), } impl Wallet { pub fn id(&self) -> WalletId { match self { Wallet::V0(v0) => v0.id, } } pub fn content_as_bytes(&self) -> Vec { match self { Wallet::V0(v0) => serde_bare::to_vec(&v0.content).unwrap(), } } pub fn sig(&self) -> Sig { match self { Wallet::V0(v0) => v0.sig, } } pub fn pazzle_length(&self) -> u8 { match self { Wallet::V0(v0) => v0.content.pazzle_length, } } } /// Add Wallet Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct AddWalletV0 { /// wallet. optional (for those who chose not to upload their wallet to nextgraph.one server) pub wallet: Option, /// bootstrap pub bootstrap: Bootstrap, } /// Add Wallet #[derive(Clone, Debug, Serialize, Deserialize)] pub enum AddWallet { V0(AddWalletV0), } impl AddWallet { pub fn id(&self) -> BootstrapId { match self { AddWallet::V0(v0) => v0.bootstrap.id(), } } pub fn bootstrap(&self) -> &Bootstrap { match self { AddWallet::V0(v0) => &v0.bootstrap, } } pub fn wallet(&self) -> Option<&Wallet> { match self { AddWallet::V0(v0) => v0.wallet.as_ref(), } } } /// Create Wallet Version 0, used by the API create_wallet_v0 #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] pub struct CreateWalletV0 { #[zeroize(skip)] #[serde(with = "serde_bytes")] pub security_img: Vec, pub security_txt: String, pub pin: [u8; 4], pub pazzle_length: u8, #[zeroize(skip)] pub send_bootstrap: Option, #[zeroize(skip)] pub send_wallet: bool, #[zeroize(skip)] pub result_with_wallet_file: bool, #[zeroize(skip)] pub local_save: bool, #[zeroize(skip)] pub peer_id: PubKey, #[zeroize(skip)] pub nonce: u64, } impl CreateWalletV0 { pub fn new( security_img: Vec, security_txt: String, pin: [u8; 4], pazzle_length: u8, send_bootstrap: Option, send_wallet: bool, peer_id: PubKey, nonce: u64, ) -> Self { CreateWalletV0 { result_with_wallet_file: false, local_save: true, security_img, security_txt, pin, pazzle_length, send_bootstrap, send_wallet, peer_id, nonce, } } } #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] pub struct CreateWalletResultV0 { #[zeroize(skip)] pub wallet: Wallet, #[serde(with = "serde_bytes")] #[zeroize(skip)] pub wallet_file: Vec, pub pazzle: Vec, pub mnemonic: [u16; 12], #[zeroize(skip)] pub wallet_name: String, } #[derive(Debug, Eq, PartialEq, Clone)] pub enum NgWalletError { InvalidPin, InvalidPazzle, InvalidPazzleLength, InvalidSecurityImage, InvalidSecurityText, SubmissionError, InternalError, EncryptionError, DecryptionError, InvalidSignature, } impl fmt::Display for NgWalletError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NgFileV0 { Wallet(Wallet), } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NgFile { V0(NgFileV0), } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ShuffledPazzle { pub category_indices: Vec, pub emoji_indices: Vec>, }