UNPKG

micro-stacks

Version:

Tiny libraries for building Stacks apps.

46 lines (41 loc) 9.21 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var bip39 = require('@scure/bip39'); var bip32 = require('@scure/bip32'); var english = require('@scure/bip39/wordlists/english'); var common = require('micro-stacks/common'); var cryptoSha = require('micro-stacks/crypto-sha'); var crypto = require('micro-stacks/crypto'); var cryptoAes = require('micro-stacks/crypto-aes'); var cryptoHmacSha = require('micro-stacks/crypto-hmac-sha'); var cryptoPbkdf2 = require('micro-stacks/crypto-pbkdf2'); var storage = require('micro-stacks/storage'); var A="m/888'/0'",S="m/44/5757'/0'/1",_="m/44'/5757'/0'/0",v="wallet-config.json",I="https://hub.blockstack.org";function D(e){return e.length===32||e.length===33&&(e[0]===2||e[0]===3)}function p(e){return e+bip32.HARDENED_OFFSET}var U=e=>{let t=e.derive(S).privateKey;if(!t)throw new TypeError("Unable to derive config key for wallet identities");return common.bytesToHex(t)},te=e=>{let o=bip32.HDKey.fromExtendedKey(e).deriveChild(p(45)).privateKey;if(!o)throw new TypeError("Unable to derive config key for wallet identities");return common.bytesToHex(o)};function N(e){let t=e.derive(A).publicKey;if(!t)throw new TypeError("Unable to derive public key from data derivation path");return common.bytesToHex(cryptoSha.hashSha256(common.utf8ToBytes(common.bytesToHex(t))))}function b(e){if(!e.privateKey)throw Error("no private key");let t=N(e),r=e.privateExtendedKey,o=U(e);return {salt:t,rootKey:r,configPrivateKey:o}}async function T(e,t,r=crypto.getRandomBytes(16)){if(!bip39.validateMnemonic(e,english.wordlist))throw new Error("Not a valid bip39 mnemonic");let o=await cryptoPbkdf2.createPbkdf2(),i=bip39.mnemonicToEntropy(e,english.wordlist),n=await o.derive(t,r,1e5,48,"sha512"),a=n.slice(0,16),s=n.slice(16,32),c=n.slice(32,48),f=await cryptoAes.aes128CbcEncrypt(c,a,i),m=common.concatByteArrays([r,f]),y=cryptoHmacSha.hmacSha256(s,m);return common.concatByteArrays([r,y,f])}function P(e,t,r){let o=e.derive(_).deriveChild(t);if(!o.privateKey)throw Error("no private key");let n=e.derive(A).deriveChild(p(t));if(!n.privateKey)throw new Error("Must have private key to derive identities");let a=common.bytesToHex(n.privateKey),s=n.deriveChild(p(0)).privateExtendedKey;return {stxPrivateKey:`${common.bytesToHex(o.privateKey)}${D(o.privateKey)?"01":""}`,dataPrivateKey:a,appsKey:s,salt:r,index:t}}function w(e){return P(bip32.HDKey.fromExtendedKey(e.rootKey),e.accounts.length,e.salt)}function H(e,t,r){let o=[],i=[...Array(t).keys()];for(let n of i){let s=(r||(e.accounts.length>0?e.accounts.length-1:0))+n;e.accounts[s]||o.push(P(bip32.HDKey.fromExtendedKey(e.rootKey),s,e.salt));}return o}function ye(e=256){if(e!==256&&e!==128)throw TypeError(`Incorrect entropy bits provided, expected 256 or 128 (24 or 12 word results), got: "${String(e)}".`);return bip39.generateMnemonic(english.wordlist,e)}async function ue(e,t){let[r,o]=await Promise.all([bip39.mnemonicToSeed(e),T(e,t)]),i=common.bytesToHex(o),n=b(bip32.HDKey.fromMasterSeed(r));return L({...n,encryptedSecretKey:i,accounts:[]})}function L(e){return {...e,accounts:[...e.accounts,w(e)]}}function xe(e,t=crypto.StacksNetworkVersion.mainnetP2PKH){let r=e.stxPrivateKey.endsWith("01");return crypto.privateKeyToStxAddress(e.stxPrivateKey.slice(0,64),t,r)}var Ae={private:76066276,public:76067358};function ve(e){let t=0;if(e.length===0)return t;for(let r=0;r<e.length;r++){let o=e.charCodeAt(r);t=(t<<5)-t+o,t&=t;}return t&2147483647}function he(e,t,r=!0){if(r)return Ke(e,t);let o=cryptoSha.hashSha256(common.utf8ToBytes(`${t}${e.salt}`)),i=bip32.HDKey.fromExtendedKey(e.appsKey);if(!i.privateKey)throw Error("no rootNode.privateKey");let n=new bip32.HDKey({privateKey:i.privateKey,chainCode:o,versions:Ae}).deriveChild(p(0));if(!n.privateKey)throw new Error("[micro-stacks/wallet-sdk] getAppPrivateKey: No private key found");return common.bytesToHex(n.privateKey)}function Ke(e,t){let r=common.bytesToHex(cryptoSha.hashSha256(common.utf8ToBytes(`${t}${e.salt}`))),o=ve(r),i=bip32.HDKey.fromExtendedKey(e.appsKey).deriveChild(p(o));if(!i.privateKey)throw new Error("[micro-stacks/wallet-sdk] getLegacyAppPrivateKey: No private key found");return common.bytesToHex(i.privateKey)}function be(e){let t=e.stxPrivateKey.endsWith("01");return crypto.publicKeyToBase58Address(crypto.getPublicKey(e.dataPrivateKey,t))}async function _e(e,t){let r=typeof e=="string"?common.hexToBytes(e):e,o=r.slice(0,16),i=r.slice(16,48),n=r.slice(48),a=common.concatByteArrays([o,n]),c=await(await cryptoPbkdf2.createPbkdf2()).derive(t,o,1e5,48,"sha512"),f=c.slice(0,16),m=c.slice(16,32),y=c.slice(32,48),d=await cryptoAes.aes128CbcDecrypt(y,f,n),W=cryptoHmacSha.hmacSha256(m,a),g=cryptoSha.hashSha256(i),x=cryptoSha.hashSha256(W);if(common.bytesToHex(g)!==common.bytesToHex(x))throw new Error("Wrong password (HMAC mismatch)");let l;try{l=bip39.entropyToMnemonic(d,english.wordlist);}catch(C){throw console.error("Error thrown by `entropyToMnemonic`"),console.error(C),new Error("Wrong password (invalid plaintext)")}if(!bip39.validateMnemonic(l,english.wordlist))throw new Error("Wrong password (invalid plaintext)");return l}function Fe(e){return `did:btc-addr:${e}`}function Oe(){return new Date(new Date().setMonth(new Date().getMonth()+1))}function Le(){let e=new Date().getTime();return typeof performance<"u"&&typeof performance.now=="function"&&(e+=performance.now()),"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let r=(e+Math.random()*16)%16|0;return e=Math.floor(e/16),(t==="x"?r:r&3|8).toString(16)})}async function j(e,t){let r=await crypto.encryptECIES({content:common.hexToBytes(t),publicKey:e,cipherTextEncoding:"hex",wasString:!1}),o=JSON.stringify(r);return common.bytesToHex(common.utf8ToBytes(o))}async function Rt(e){let{expiresAt:t}=e,{privateKey:r,appPrivateKey:o,coreToken:i,transitPublicKey:n,metadata:a,hubUrl:s,blockstackAPIUrl:c,associationToken:f,username:m,profile:y}=e,d=common.bytesToHex(crypto.getPublicKey(r)),W=crypto.publicKeyToStxAddress(d);t||(t=Oe().getTime());let g=o,x=i,l={};o!=null&&(n!=null&&(g=await j(n,o),i!=null&&(x=await j(n,i))),l={email:a!=null&&a.email?a.email:null,profile_url:a!=null&&a.profileUrl?a.profileUrl:null,hubUrl:s,blockstackAPIUrl:c,associationToken:f,version:"undefined"});let C={jti:Le(),iat:Math.floor(new Date().getTime()/1e3),exp:Math.floor(t/1e3),iss:Fe(W),private_key:g??null,public_keys:[d],profile:y??null,username:m??null,core_token:x??null,...l};return new crypto.TokenSigner("ES256k",r).sign(C)}function K(e,t=I){return storage.generateGaiaHubConfig({gaiaHubUrl:t,privateKey:e})}async function q(e,t){try{let r=t==null?void 0:t.gaiaHubConfig;r||(r=await K(e,t==null?void 0:t.gaiaHubUrl));let o=await storage.getFile(v,{privateKey:e,gaiaHubConfig:r});if(typeof o!="string"){console.error("Wallet config response should be of type string");return}return JSON.parse(o)}catch{}}function z(e){return {accounts:e.accounts.map(t=>({username:t.username,apps:{}}))}}async function u({walletConfig:e,privateKey:t,gaiaHubConfig:r,gaiaHubUrl:o}){r||(r=await K(t,o)),await storage.putFile(v,JSON.stringify(e),{gaiaHubConfig:r,privateKey:t,encrypt:!0,sign:!0});}async function sr({wallet:e,gaiaHubConfig:t,gaiaHubUrl:r}){let o=await q(e.configPrivateKey,{gaiaHubConfig:t,gaiaHubUrl:r});return o||(o=z(e),await u({walletConfig:o,gaiaHubConfig:t,gaiaHubUrl:r,privateKey:e.configPrivateKey})),o}function fr({wallet:e,walletConfig:t}){if(!t||t.accounts.length===0)return e;let r=t.accounts.length,o=H(e,r);return {...e,accounts:o}}function $e({wallet:e,walletConfig:t,app:r,account:o}){for(let i of e.accounts){let n=0,a=t.accounts[n];if(a?(a.apps=a.apps||{},a.username=i==null?void 0:i.username,t.accounts[n]=a):t.accounts.push({username:i==null?void 0:i.username,apps:{}}),n===o.index){let s=t.accounts[n];s.apps=s.apps||{},s.apps[r.origin]=r,t.accounts[n]=s;}n+=1;}return t}var yr=async({wallet:e,account:t,app:r,walletConfig:o,gaiaHubConfig:i})=>{let n=$e({wallet:e,walletConfig:o,app:r,account:t});return await u({walletConfig:n,gaiaHubConfig:i,privateKey:e.configPrivateKey}),o},ur=async({wallet:e,walletConfig:t,gaiaHubConfig:r})=>{let o={...t,accounts:[...t.accounts,{apps:{}}]};return await u({walletConfig:o,gaiaHubConfig:r,privateKey:e.configPrivateKey}),t}; exports.addNewAccountToWalletConfig = ur; exports.createWalletConfigGaiaHubConfig = K; exports.decryptMnemonic = _e; exports.deriveAccount = P; exports.deriveConfigPrivateKey = U; exports.deriveLegacyConfigPrivateKey = te; exports.deriveManyAccountsForWallet = H; exports.deriveNextAccountFromWallet = w; exports.deriveSalt = N; exports.deriveWalletKeys = b; exports.encryptMnemonic = T; exports.encryptPrivateKey = j; exports.fetchWalletConfig = q; exports.generateAndInsertNewAccount = L; exports.generateSecretKey = ye; exports.generateWallet = ue; exports.getAppPrivateKey = he; exports.getGaiaAddress = be; exports.getOrSetWalletConfig = sr; exports.getStxAddressFromAccount = xe; exports.makeAuthResponse = Rt; exports.makeDIDFromAddress = Fe; exports.makeUUID4 = Le; exports.makeWalletConfig = z; exports.nextMonth = Oe; exports.restoreWalletAccountsFromWalletConfig = fr; exports.saveWalletConfig = u; exports.updateWalletConfigWithApp = yr;