wowok 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/w/call/repository.js +1 -1
- package/dist/cjs/w/messenger/messenger-manager.js +1 -1
- package/dist/cjs/w/messenger/messenger.js +1 -1
- package/dist/cjs/w/messenger/pq/crypto.d.ts +39 -0
- package/dist/cjs/w/messenger/pq/crypto.js +1 -0
- package/dist/cjs/w/messenger/pq/erasure.d.ts +17 -0
- package/dist/cjs/w/messenger/pq/erasure.js +1 -0
- package/dist/cjs/w/messenger/pq/index.d.ts +9 -0
- package/dist/cjs/w/messenger/pq/index.js +1 -0
- package/dist/cjs/w/messenger/pq/keycombiner.d.ts +1 -0
- package/dist/cjs/w/messenger/pq/keycombiner.js +1 -0
- package/dist/cjs/w/messenger/pq/pqxdh.d.ts +41 -0
- package/dist/cjs/w/messenger/pq/pqxdh.js +1 -0
- package/dist/cjs/w/messenger/pq/ratchet.d.ts +12 -0
- package/dist/cjs/w/messenger/pq/ratchet.js +1 -0
- package/dist/cjs/w/messenger/pq/session.d.ts +39 -0
- package/dist/cjs/w/messenger/pq/session.js +1 -0
- package/dist/cjs/w/messenger/pq/spqr.d.ts +23 -0
- package/dist/cjs/w/messenger/pq/spqr.js +1 -0
- package/dist/cjs/w/messenger/pq/store-adapter.d.ts +33 -0
- package/dist/cjs/w/messenger/pq/store-adapter.js +1 -0
- package/dist/cjs/w/messenger/pq/types.d.ts +181 -0
- package/dist/cjs/w/messenger/pq/types.js +1 -0
- package/dist/cjs/w/messenger/session.d.ts +15 -16
- package/dist/cjs/w/messenger/session.js +1 -1
- package/dist/cjs/w/messenger/storage.d.ts +2 -0
- package/dist/cjs/w/messenger/storage.js +1 -1
- package/dist/cjs/w/messenger/types.d.ts +14 -0
- package/dist/cjs/w/messenger/utils.js +1 -1
- package/dist/cjs/w/query/object.js +1 -1
- package/dist/esm/w/call/repository.js +1 -1
- package/dist/esm/w/messenger/messenger-manager.js +1 -1
- package/dist/esm/w/messenger/messenger.js +1 -1
- package/dist/esm/w/messenger/pq/crypto.d.ts +39 -0
- package/dist/esm/w/messenger/pq/crypto.js +1 -0
- package/dist/esm/w/messenger/pq/erasure.d.ts +17 -0
- package/dist/esm/w/messenger/pq/erasure.js +1 -0
- package/dist/esm/w/messenger/pq/index.d.ts +9 -0
- package/dist/esm/w/messenger/pq/index.js +1 -0
- package/dist/esm/w/messenger/pq/keycombiner.d.ts +1 -0
- package/dist/esm/w/messenger/pq/keycombiner.js +1 -0
- package/dist/esm/w/messenger/pq/pqxdh.d.ts +41 -0
- package/dist/esm/w/messenger/pq/pqxdh.js +1 -0
- package/dist/esm/w/messenger/pq/ratchet.d.ts +12 -0
- package/dist/esm/w/messenger/pq/ratchet.js +1 -0
- package/dist/esm/w/messenger/pq/session.d.ts +39 -0
- package/dist/esm/w/messenger/pq/session.js +1 -0
- package/dist/esm/w/messenger/pq/spqr.d.ts +23 -0
- package/dist/esm/w/messenger/pq/spqr.js +1 -0
- package/dist/esm/w/messenger/pq/store-adapter.d.ts +33 -0
- package/dist/esm/w/messenger/pq/store-adapter.js +1 -0
- package/dist/esm/w/messenger/pq/types.d.ts +181 -0
- package/dist/esm/w/messenger/pq/types.js +1 -0
- package/dist/esm/w/messenger/session.d.ts +15 -16
- package/dist/esm/w/messenger/session.js +1 -1
- package/dist/esm/w/messenger/storage.d.ts +2 -0
- package/dist/esm/w/messenger/storage.js +1 -1
- package/dist/esm/w/messenger/types.d.ts +14 -0
- package/dist/esm/w/messenger/utils.js +1 -1
- package/dist/esm/w/query/object.js +1 -1
- package/package.json +43 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash}from'crypto';import*as a183a from'fs';import*as a183b from'path';import{MessengerSession,DecryptionEngine}from'./session.js';import{MessengerServerClient}from'./server.js';import{MessageStorage,SessionStateStorage}from'./storage.js';import{hashPlaintext,verifyMessage,bytesToBase64}from'./crypto.js';import{canonicalizeJson}from'./utils.js';import{DEFAULT_MESSENGER_CONFIG,MessengerError,MessengerErrorCode,MessageDirection,MessageStatus,WTS_FILE_BYTES_LIMIT,NORMAL_MESSAGE_BYTES_LIMIT,CHAIN_PROOF_TYPE}from'./types.js';import{isValidWowAddress}from'../../utils/sui-types.js';async function getAccount(){const {Account:a}=await import('../local/account.js');return a['Instance']();}export class Messenger{['session'];['serverClient'];['config'];['userAddress']=null;['onMessageCallback']=null;['pollingTimer']=null;['messageConsecutiveEmptyPulls']=0x0;['prekeyConsecutiveOkCount']=0x0;['currentMessageInterval'];['isPollingRunning']=![];['messageStorage'];['sessionStateStorage'];['decryptionEngine'];['waitingMessages']=new Map();['failedMessages']=new Map();['waitingMessageRetries']=new Map();constructor(a,b){this['userAddress']=a,this['config']={...DEFAULT_MESSENGER_CONFIG,...b},this['session']=new MessengerSession(a,this['config']),this['serverClient']=new MessengerServerClient(this['config']),this['messageStorage']=new MessageStorage(a),this['sessionStateStorage']=new SessionStateStorage(a),this['decryptionEngine']=new DecryptionEngine(this['session']['store']),this['currentMessageInterval']=this['config']['message_poll_default_interval_ms']??0x6*0x3c*0x3e8;}['getUserAddress'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');return this['userAddress'];}async['getPublicKey'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await(await getAccount())['get'](this['userAddress'],![]);return a?.['pubkey']||'';}['isValidGuardMessage'](a,b){return Boolean(a&&b&&isValidWowAddress(a)&&isValidWowAddress(b));}async['generateSignatureParams'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b=await(await getAccount())['get'](this['userAddress'],![]);if(!b?.['secret'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20not\x20found\x20or\x20no\x20secret\x20key\x20for\x20'+this['userAddress']);const {Ed25519Keypair:c}=await import('../../keypairs/ed25519/index.js'),d=c['fromSecretKey'](b['secret']),e=d['getPublicKey'](),f=e['toWPublicKey'](),g=Date['now'](),h=this['generateNonce'](),i=a+':'+f+':'+g+':'+h,j=await d['sign'](new TextEncoder()['encode'](i));return{'signatureScheme':'ED25519','signature':Buffer['from'](j)['toString']('base64'),'timestamp':g,'nonce':h};}['generateNonce'](){const a=new Uint8Array(0x10);return crypto['getRandomValues'](a),Array['from'](a,c=>c['toString'](0x10)['padStart'](0x2,'0'))['join']('');}async['initialize'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');await this['session']['ensureIdentity'](this['userAddress']),await this['session']['registerDevice'](this['userAddress']),await this['session']['ensurePreKeys'](this['userAddress']),await this['checkAndRefillPrekeys'](),this['startPollingTimer']();}async['checkAndRefillPrekeys'](){if(!this['userAddress'])return;await this['session']['ensurePreKeys'](this['userAddress'],![]);}['startPollingTimer'](){this['pollingTimer']&&clearTimeout(this['pollingTimer']),this['isPollingRunning']=!![],this['scheduleNextPoll']();}['stopPollingTimer'](){this['isPollingRunning']=![],this['pollingTimer']&&(clearTimeout(this['pollingTimer']),this['pollingTimer']=null);}['scheduleNextPoll'](){if(!this['isPollingRunning'])return;const a=this['getNextPollInterval']();this['pollingTimer']=setTimeout(()=>{this['poll']()['catch'](b=>{console['error']('[Polling]\x20Error\x20during\x20poll:',b);})['finally'](()=>{this['scheduleNextPoll']();});},a);}['getNextPollInterval'](){if(this['waitingMessages']['size']>0x0)return this['config']['message_poll_waiting_interval_ms']??0x3*0x3e8;return this['currentMessageInterval'];}async['poll'](){if(!this['userAddress'])return;try{const a=await this['pullMessages']();a['messages']['length']>0x0?(this['messageConsecutiveEmptyPulls']=0x0,this['currentMessageInterval']=this['config']['message_poll_fast_interval_ms']??0x6*0x3e8,this['onMessageCallback']&&this['onMessageCallback'](a['messages'])):(this['messageConsecutiveEmptyPulls']++,this['messageConsecutiveEmptyPulls']>=(this['config']['message_poll_consecutive_empty_limit']??0x3)&&(this['currentMessageInterval']=this['config']['message_poll_default_interval_ms']??0x6*0x3c*0x3e8,this['messageConsecutiveEmptyPulls']=0x0));if(a['prekey_status']){const b=a['prekey_status'];b['shouldRefill']?(this['prekeyConsecutiveOkCount']=0x0,await this['checkAndRefillPrekeys']()):(this['prekeyConsecutiveOkCount']++,this['prekeyConsecutiveOkCount']>=(this['config']['prekey_poll_consecutive_ok_limit']??0x3)&&(this['prekeyConsecutiveOkCount']=0x0));}}catch(c){console['error']('[Polling]\x20Poll\x20error:',c);}}['triggerFastPoll'](){this['currentMessageInterval']=this['config']['message_poll_fast_interval_ms']??0x6*0x3e8,this['messageConsecutiveEmptyPulls']=0x0,this['isPollingRunning']&&(this['pollingTimer']&&clearTimeout(this['pollingTimer']),this['scheduleNextPoll']());}async['sendMessage'](a,b,c){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const d=Date['now'](),e=Date['now'](),f=this['generateNonce'](),g=new TextEncoder()['encode'](b)['length'];if(g>NORMAL_MESSAGE_BYTES_LIMIT)throw new MessengerError(MessengerErrorCode['INVALID_INPUT'],'Message\x20size\x20('+g+'\x20bytes)\x20exceeds\x20maximum\x20allowed\x20size\x20('+NORMAL_MESSAGE_BYTES_LIMIT+'\x20bytes).\x20Consider\x20using\x20sendZipFile\x20for\x20large\x20content.');await this['pullMessages']();const h=this['messageStorage']['getLastReceivedLeafIndex'](this['userAddress'],a),i=await this['session']['encryptMessage'](this['userAddress'],a,b),j=Buffer['from'](new Uint8Array(i['body']))['toString']('base64'),k=hashPlaintext(b,d,c?.['guardAddress'],c?.['passportAddress'],h),l=await(await getAccount())['get'](this['userAddress'],![]);if(!l)throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20account\x20not\x20found');const m=l['pubkey'],n=c?.['guardAddress']||'',o=c?.['passportAddress']||'',p=h>=0x0?h['toString']():'',q=c?.['force']?'true':'false',r='send_message:'+this['userAddress']+':'+a+':'+n+':'+o+':'+k+':'+p+':'+m+':'+e+':'+f+':'+q,s=await(await getAccount())['signData'](this['userAddress'],r),t=Buffer['from'](s['signature']['slice'](0x2),'hex'),u=bytesToBase64(new Uint8Array(t)),v=await this['serverClient']['sendMessage']({'sender':s['publicKey'],'recipient':a,'ciphertext':j,'plaintextHash':k,'clientTimestamp':d,'msgType':i['type'],'signatureScheme':'ED25519','signature':u,'timestamp':e,'nonce':f,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'force':c?.['force'],'lastReceivedLeafIndex':h>=0x0?h:undefined}),w={'messageId':v['messageId'],'fromAddress':this['userAddress'],'toAddress':a,'plaintextHash':k,'plaintext':b,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'lastReceivedLeafIndex':h,'direction':MessageDirection['SENT'],'status':v['status']==='confirmed'?MessageStatus['CONFIRMED']:MessageStatus['PENDING'],'msgType':i['type'],'createdAt':d};v['merkleData']&&(w['leafIndex']=v['merkleData']['leafIndex'],w['prevRoot']=v['merkleData']['prevRoot'],w['newRoot']=v['merkleData']['newRoot'],w['serverSignature']=v['merkleData']['serverSignature'],w['serverTimestamp']=v['merkleData']['serverTimestamp'],w['serverPublicKey']=v['merkleData']['serverPublicKey'],this['sessionStateStorage']['updateSessionState'](this['userAddress'],a,{'currentRoot':v['merkleData']['newRoot'],'prevRoot':v['merkleData']['prevRoot'],'lastLeafIndex':v['merkleData']['leafIndex'],'lastSyncAt':Date['now']()}));this['messageStorage']['saveMessage'](w);if(v['pendingMerkleData'])for(const [x,y]of Object['entries'](v['pendingMerkleData'])){const z=this['messageStorage']['getMessage'](x);z&&z['status']===MessageStatus['PENDING']&&this['messageStorage']['updateMessageStatus'](x,MessageStatus['CONFIRMED'],{'leafIndex':y['leafIndex'],'newRoot':y['newRoot'],'serverSignature':y['serverSignature'],'serverTimestamp':y['serverTimestamp'],'serverPublicKey':y['serverPublicKey']});}return this['triggerFastPoll'](),{'messageId':v['messageId'],'status':w['status'],'merkleData':v['merkleData'],'guardList':v['guard_list'],'lastReceivedLeafIndex':h>=0x0?h:undefined};}async['pullMessages'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b=Date['now'](),c=this['generateNonce'](),d='fetch_messages:'+this['userAddress']+':'+(a||0x64)+':'+b+':'+c,e=await(await getAccount())['get'](this['userAddress'],![]),f=e?.['pubkey']||'',g=await(await getAccount())['signData'](this['userAddress'],d),h=Buffer['from'](g['signature']['slice'](0x2),'hex'),i={'signatureScheme':'ED25519','signature':bytesToBase64(h),'timestamp':b,'nonce':c},j=await this['serverClient']['pullMessages'](this['userAddress'],f,i,a);if(j['messages']['length']===0x0)return{'messages':[],'prekey_status':j['prekey_status']};const k=[],l=[],m=[...j['messages']]['sort'](function(n,o){return(n['clientTimestamp']||0x0)-(o['clientTimestamp']||0x0);});await this['processWaitingMessages'](k,l);for(const n of m){if(this['waitingMessages']['has'](n['id'])){l['push'](n['id']);continue;}if(this['failedMessages']['has'](n['id'])){l['push'](n['id']);continue;}const o=this['messageStorage']['getMessage'](n['id']);if(o){l['push'](n['id']);continue;}const p=await this['tryDecryptMessage'](n);if(p['success']&&p['message']&&p['decryptedData'])this['messageStorage']['saveMessage'](p['message']),k['push'](p['decryptedData']),l['push'](n['id']),this['waitingMessages']['delete'](n['id']);else p['shouldRetry']?(console['warn']('[Messenger]\x20消息\x20'+n['id']+'\x20解密失败,加入等待池:\x20'+p['error']),this['waitingMessages']['set'](n['id'],n)):(console['error']('[Messenger]\x20消息\x20'+n['id']+'\x20解密永久失败:\x20'+p['error']),this['failedMessages']['set'](n['id'],{'message':n,'error':p['error']||'Unknown\x20error'}),await this['saveFailedMessage'](n,p['error']||'Unknown\x20error'),l['push'](n['id']));}this['waitingMessages']['size']>0x0&&this['isPollingRunning']&&this['reschedulePolling']();if(l['length']>0x0&&this['userAddress']){const q=Date['now'](),r=this['generateNonce'](),s=await(await getAccount())['get'](this['userAddress'],![]);if(s?.['pubkey']){const t='ack_messages:'+this['userAddress']+':'+l['join'](',')+':'+s['pubkey']+':'+q+':'+r,u=await(await getAccount())['signData'](this['userAddress'],t),v=Buffer['from'](u['signature']['slice'](0x2),'hex'),w=bytesToBase64(new Uint8Array(v));await this['serverClient']['acknowledgeMessages'](l,u['publicKey'],{'publicKey':u['publicKey'],'signatureScheme':'ED25519','signature':w,'timestamp':q,'nonce':r});}}return{'messages':k,'prekey_status':j['prekey_status']};}['getLastSentLeafIndex'](a){if(!this['userAddress'])return-0x1;const b=this['messageStorage']['getMessagesBySession'](this['userAddress'],a),c=b['filter'](d=>d['direction']===MessageDirection['SENT']&&d['leafIndex']!==undefined);if(c['length']===0x0)return-0x1;return Math['max'](...c['map'](d=>d['leafIndex']));}['getSessionMessages'](a){if(!this['userAddress'])return[];return this['messageStorage']['getMessagesBySession'](this['userAddress'],a);}['getSessionState'](a){if(!this['userAddress'])return undefined;return this['sessionStateStorage']['getSessionState'](this['userAddress'],a);}async['submitChainProof'](a,b,c){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const d=this['sessionStateStorage']['getSessionState'](this['userAddress'],b);if(!d||!d['currentRoot'])throw new MessengerError(MessengerErrorCode['SESSION_NOT_ESTABLISHED'],'No\x20session\x20state\x20found');const e=this['messageStorage']['getMessagesBySession'](this['userAddress'],b),f=e[e['length']-0x1];if(!f||!f['serverPublicKey']||!f['serverSignature'])throw new MessengerError(MessengerErrorCode['INVALID_PROOF'],'No\x20temporal\x20proof\x20(missing\x20server\x20public\x20key\x20or\x20signature),\x20cannot\x20submit\x20Proof\x20to\x20WoWok\x20blockchain');const g=f['serverPublicKey'],h=f['serverSignature'],i={'sessionId':d['sessionId'],'merkleRoot':d['currentRoot'],'messageCount':d['messageCount'],'timestamp':Date['now']()},{CallProof:j}=await import('../call/proof.js'),k=new j({'proof':JSON['stringify'](i),'server_pubkey':g,'server_signature':h,'proof_type':CHAIN_PROOF_TYPE,'description':c||'Messenger\x20session\x20proof\x20for\x20'+this['userAddress']+'\x20<->\x20'+b,'item_count':d['messageCount'],'about_address':d['sessionId']}),l=await k['call'](a),m=l?.['objectChanges'],n=m?.['find'](o=>o['objectType']?.['includes']('Proof'))?.['objectId'];return{'proofAddress':n||'','txHash':l?.['digest']||''};}['destroy'](){this['stopPollingTimer']();}['disconnect'](){this['destroy']();}['setOnMessageCallback'](a){this['onMessageCallback']=a;}async['sendZipFile'](a,b,c){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const d=await this['loadFileData'](b),{ZipWriter:e,BlobWriter:f,Uint8ArrayReader:g}=await import('@zip.js/zip.js'),h=c?.['fileName']||a183b['basename'](b)||'file.zip',i=c?.['contentType']||this['detectContentType'](h),j=new f('application/zip'),k=new e(j);await k['add'](h,new g(d)),await k['close']();const l=await j['getData'](),m=new Uint8Array(await l['arrayBuffer']());if(m['length']>WTS_FILE_BYTES_LIMIT)throw new MessengerError(MessengerErrorCode['INVALID_INPUT'],'ZIP\x20file\x20size\x20('+m['length']+'\x20bytes)\x20exceeds\x20local\x20maximum\x20allowed\x20size\x20('+WTS_FILE_BYTES_LIMIT+'\x20bytes)');const n='0x'+createHash('sha256')['update'](m)['digest']('hex'),o=Buffer['from'](m)['toString']('base64'),p={'fileName':h+'.zip','fileSize':m['length'],'fileHash':n,'contentType':i};await this['pullMessages']();const q=this['messageStorage']['getLastReceivedLeafIndex'](this['userAddress'],a),r=Date['now'](),s=await this['session']['encryptMessage'](this['userAddress'],a,o),t=Buffer['from'](new Uint8Array(s['body']))['toString']('base64'),u=c?.['guardAddress']||'',v=c?.['passportAddress']||'',w=hashPlaintext(o,r,u,v,q),x=Date['now'](),y=this['generateNonce'](),z=await(await getAccount())['get'](this['userAddress'],![]);if(!z)throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20account\x20not\x20found');const A=z['pubkey'],B=q>=0x0?q['toString']():'',C=c?.['force']?'true':'false',D='send_message:'+this['userAddress']+':'+a+':'+u+':'+v+':'+w+':'+B+':'+A+':'+x+':'+y+':'+C,E=await(await getAccount())['signData'](this['userAddress'],D),F=Buffer['from'](E['signature']['slice'](0x2),'hex'),G=bytesToBase64(new Uint8Array(F)),H=await this['serverClient']['sendMessage']({'sender':E['publicKey'],'recipient':a,'ciphertext':t,'plaintextHash':w,'clientTimestamp':r,'msgType':s['type'],'zipMetadata':p,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'force':c?.['force'],'lastReceivedLeafIndex':q>=0x0?q:undefined,'signatureScheme':'ED25519','signature':G,'timestamp':x,'nonce':y}),I={'messageId':H['messageId'],'fromAddress':this['userAddress'],'toAddress':a,'plaintextHash':w,'plaintext':o,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'lastReceivedLeafIndex':q,'direction':MessageDirection['SENT'],'status':H['status']==='confirmed'?MessageStatus['CONFIRMED']:MessageStatus['PENDING'],'msgType':s['type'],'zipMetadata':p,'createdAt':r};H['merkleData']&&(I['leafIndex']=H['merkleData']['leafIndex'],I['prevRoot']=H['merkleData']['prevRoot'],I['newRoot']=H['merkleData']['newRoot'],I['serverSignature']=H['merkleData']['serverSignature'],I['serverTimestamp']=H['merkleData']['serverTimestamp'],I['serverPublicKey']=H['merkleData']['serverPublicKey'],this['sessionStateStorage']['updateSessionState'](this['userAddress'],a,{'currentRoot':H['merkleData']['newRoot'],'prevRoot':H['merkleData']['prevRoot'],'lastLeafIndex':H['merkleData']['leafIndex'],'lastSyncAt':Date['now']()}));this['messageStorage']['saveMessage'](I);if(H['pendingMerkleData'])for(const [J,K]of Object['entries'](H['pendingMerkleData'])){const L=this['messageStorage']['getMessage'](J);L&&L['status']===MessageStatus['PENDING']&&this['messageStorage']['updateMessageStatus'](J,MessageStatus['CONFIRMED'],{'leafIndex':K['leafIndex'],'newRoot':K['newRoot'],'serverSignature':K['serverSignature'],'serverTimestamp':K['serverTimestamp'],'serverPublicKey':K['serverPublicKey']});}return this['triggerFastPoll'](),{'messageId':H['messageId'],'status':I['status'],'merkleData':H['merkleData'],'guardList':H['guard_list'],'lastReceivedLeafIndex':q>=0x0?q:undefined};}async['loadFileData'](a){if(a['startsWith']('http://')||a['startsWith']('https://')){const d=await fetch(a);if(!d['ok'])throw new MessengerError(MessengerErrorCode['NETWORK_ERROR'],'Failed\x20to\x20fetch\x20file\x20from\x20'+a+':\x20'+d['statusText']);const e=await d['arrayBuffer']();return new Uint8Array(e);}const b=a183b['resolve'](a);if(!a183a['existsSync'](b))throw new MessengerError(MessengerErrorCode['FILE_NOT_FOUND'],'File\x20not\x20found:\x20'+b);const c=a183a['readFileSync'](b);return new Uint8Array(c);}['detectContentType'](a){const b=a183b['extname'](a)['toLowerCase']();switch(b){case'.wts':return'wts';case'.wip':return'wip';default:return'zip';}}async['_createSignedRequest'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b=Date['now'](),c=this['generateNonce'](),d=canonicalizeJson(a),e=b+':'+c+':'+d,f=await(await getAccount())['signData'](this['userAddress'],e),g=Buffer['from'](f['signature']['slice'](0x2),'hex'),h=bytesToBase64(new Uint8Array(g)),i=await(await getAccount())['get'](this['userAddress'],![]);if(!i||!i['pubkey'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20public\x20key\x20not\x20found');return{'userAddress':this['userAddress'],'publicKey':i['pubkey'],'signatureScheme':'ED25519','signature':h,'timestamp':b,'nonce':c,'data':a};}['_validateAddresses'](a){const b=[],c=[];for(const d of a){isValidWowAddress(d)?b['push'](d):c['push'](d);}return{'valid':b,'invalid':c};}async['addToBlacklist'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'add','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['addToBlacklist'](this['userAddress'],e);}async['removeFromBlacklist'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'remove','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['removeFromBlacklist'](this['userAddress'],e);}async['clearBlacklist'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a={},b=await this['_createSignedRequest'](a);return this['serverClient']['clearBlacklist'](this['userAddress'],b);}async['getBlacklist'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getBlacklist'](this['userAddress'],a);}async['existInBlacklist'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'exist','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['existInBlacklist'](this['userAddress'],e);}async['addToFriendsList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'add','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['addToFriendsList'](this['userAddress'],e);}async['removeFromFriendsList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'remove','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['removeFromFriendsList'](this['userAddress'],e);}async['clearFriendsList'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a={},b=await this['_createSignedRequest'](a);return this['serverClient']['clearFriendsList'](this['userAddress'],b);}async['getFriendsList'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getFriendsList'](this['userAddress'],a);}async['existInFriendsList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'exist','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['existInFriendsList'](this['userAddress'],e);}async['addToGuardList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b={'guards':a},c=await this['_createSignedRequest'](b);return this['serverClient']['addToGuardList'](this['userAddress'],c);}async['removeFromGuardList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'remove','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['removeFromGuardList'](this['userAddress'],e);}async['getGuardList'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getGuardList'](this['userAddress'],a);}async['getSettings'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getSettings'](this['userAddress'],a);}async['setSettings'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b={'allowStrangerMessages':a['allowStrangerMessages'],'maxInboxSize':a['maxInboxSize']},c=await this['_createSignedRequest'](b);return this['serverClient']['updateSettings'](this['userAddress'],c);}async['tryDecryptMessage'](a){try{const b=Uint8Array['from'](Buffer['from'](a['bodyB64'],'base64')),c=await this['decryptionEngine']['decryptMessage'](this['userAddress'],a['from'],b['buffer'],a['msgType']);if(!c['success']){const h=this['isRetryableError'](c['error']||'');return{'success':![],'shouldRetry':h,'error':c['error'],'sessionUpdated':c['sessionUpdated']};}const d=c['plaintext'];if(!a['clientTimestamp'])throw new Error('Missing\x20clientTimestamp\x20for\x20message\x20'+a['id']);if(!a['plaintextHash'])throw new Error('Missing\x20plaintextHash\x20for\x20message\x20'+a['id']);const e=verifyMessage({'messageId':a['id'],'plaintext':d,'plaintextHash':a['plaintextHash'],'createdAt':a['clientTimestamp'],'guardAddress':a['guardAddress'],'passportAddress':a['passportAddress'],'lastReceivedLeafIndex':a['lastReceivedLeafIndex'],'serverSignature':a['merkleMetadata']?.['serverSignature'],'serverPublicKey':a['merkleMetadata']?.['serverPublicKey'],'merkleMetadata':a['merkleMetadata']?{'prevRoot':a['merkleMetadata']['prevRoot'],'newRoot':a['merkleMetadata']['newRoot'],'serverTimestamp':a['merkleMetadata']['serverTimestamp'],'leafIndex':a['merkleMetadata']['leafIndex'],'proofSiblings':a['merkleMetadata']['proofSiblings'],'proofIndices':a['merkleMetadata']['proofIndices']}:undefined});if(!e['valid'])throw new Error(e['error']);const f={'messageId':a['id'],'fromAddress':a['from'],'toAddress':this['userAddress'],'plaintextHash':a['plaintextHash'],'plaintext':d,'guardAddress':a['guardAddress'],'passportAddress':a['passportAddress'],'lastReceivedLeafIndex':a['lastReceivedLeafIndex'],'direction':MessageDirection['RECEIVED'],'status':MessageStatus['DECRYPTED'],'msgType':a['msgType'],'leafIndex':a['merkleMetadata']?.['leafIndex'],'prevRoot':a['merkleMetadata']?.['prevRoot'],'newRoot':a['merkleMetadata']?.['newRoot'],'serverSignature':a['merkleMetadata']?.['serverSignature'],'serverTimestamp':a['merkleMetadata']?.['serverTimestamp'],'serverPublicKey':a['merkleMetadata']?.['serverPublicKey'],'createdAt':a['clientTimestamp'],'receivedAt':Date['now'](),'zipMetadata':a['zipMetadata']};a['merkleMetadata']&&this['sessionStateStorage']['updateSessionState'](this['userAddress'],a['from'],{'currentRoot':a['merkleMetadata']['newRoot'],'prevRoot':a['merkleMetadata']['prevRoot'],'lastLeafIndex':a['merkleMetadata']['leafIndex'],'lastSyncAt':Date['now']()});const g=a['merkleMetadata']?.['serverTimestamp']||a['clientTimestamp']||Date['now']();if(a['lastReceivedLeafIndex']!==undefined&&a['lastReceivedLeafIndex']>=0x0){const i=this['messageStorage']['getMessagesBySession'](this['userAddress'],a['from'])['filter'](j=>j['direction']===MessageDirection['SENT']&&j['status']===MessageStatus['CONFIRMED']&&j['leafIndex']!==undefined&&j['leafIndex']<=a['lastReceivedLeafIndex']);for(const j of i){this['messageStorage']['updateMessageStatus'](j['messageId'],MessageStatus['READ']);}}return{'success':!![],'shouldRetry':![],'message':f,'decryptedData':{'id':a['id'],'from':a['from'],'plaintext':d,'timestamp':g,'merkleVerified':!!a['merkleMetadata'],'merkleData':a['merkleMetadata']?{'leafIndex':a['merkleMetadata']['leafIndex'],'rootHash':a['merkleMetadata']['newRoot']}:undefined},'sessionUpdated':c['sessionUpdated']};}catch(k){const l=k instanceof Error?k['message']:String(k),m=this['isRetryableError'](l);return{'success':![],'shouldRetry':m,'error':l};}}['isRetryableError'](a){const b=[/message number/i,/chain key/i,/session not found/i,/prekey not found/i,/PREKEY 竞争:我的地址较小,保留我的会话/i,/The operation failed for an operation-specific reason/i,/DOMException/i,/收到 WHISPER_MESSAGE 但无现有会话,需要等待 PREKEY_MESSAGE/i];return b['some'](c=>c['test'](a));}async['processWaitingMessages'](a,b){if(this['waitingMessages']['size']===0x0)return;console['log']('[Messenger]\x20尝试处理\x20'+this['waitingMessages']['size']+'\x20条等待消息');const c=[],d=Array['from'](this['waitingMessages']['values']())['sort'](function(e,f){return(e['clientTimestamp']||0x0)-(f['clientTimestamp']||0x0);});for(const e of d){const f=e['id'],g=await this['tryDecryptMessage'](e);if(g['success']&&g['message']&&g['decryptedData'])this['messageStorage']['saveMessage'](g['message']),a['push'](g['decryptedData']),b['push'](f),c['push'](f),this['waitingMessageRetries']['delete'](f);else{if(!g['shouldRetry'])console['error']('[Messenger]\x20等待消息\x20'+f+'\x20解密永久失败:\x20'+g['error']),this['failedMessages']['set'](f,{'message':e,'error':g['error']||'Unknown\x20error'}),await this['saveFailedMessage'](e,g['error']||'Unknown\x20error'),b['push'](f),c['push'](f),this['waitingMessageRetries']['delete'](f);else{const h=this['waitingMessageRetries']['get'](f)||0x0,i=h+0x1;i>=0x3?(console['error']('[Messenger]\x20等待消息\x20'+f+'\x20重试3次后仍失败,标记为永久失败'),this['failedMessages']['set'](f,{'message':e,'error':g['error']||'Retry\x20limit\x20exceeded'}),await this['saveFailedMessage'](e,g['error']||'Retry\x20limit\x20exceeded'),b['push'](f),c['push'](f),this['waitingMessageRetries']['delete'](f)):(this['waitingMessageRetries']['set'](f,i),console['log']('[Messenger]\x20消息\x20'+f+'\x20第\x20'+i+'/3\x20次重试失败,继续等待'));}}}for(const j of c){this['waitingMessages']['delete'](j);}c['length']>0x0&&console['log']('[Messenger]\x20成功处理\x20'+c['length']+'\x20条等待消息');}async['saveFailedMessage'](a,b){const c={'messageId':a['id'],'fromAddress':a['from'],'toAddress':this['userAddress'],'plaintextHash':a['plaintextHash']||'','guardAddress':a['guardAddress'],'passportAddress':a['passportAddress'],'lastReceivedLeafIndex':a['lastReceivedLeafIndex'],'direction':MessageDirection['RECEIVED'],'status':MessageStatus['DECRYPT_FAILED'],'msgType':a['msgType'],'leafIndex':a['merkleMetadata']?.['leafIndex'],'prevRoot':a['merkleMetadata']?.['prevRoot'],'newRoot':a['merkleMetadata']?.['newRoot'],'serverSignature':a['merkleMetadata']?.['serverSignature'],'serverTimestamp':a['merkleMetadata']?.['serverTimestamp'],'serverPublicKey':a['merkleMetadata']?.['serverPublicKey'],'createdAt':a['clientTimestamp']||Date['now'](),'receivedAt':Date['now'](),'zipMetadata':a['zipMetadata'],'decryptError':b,'decryptAttempts':0x1,'lastDecryptAttemptAt':Date['now']()};this['messageStorage']['saveMessage'](c);}['reschedulePolling'](){if(!this['isPollingRunning']||!this['pollingTimer'])return;console['log']('[Messenger]\x20重新调度轮询,切换到快速模式(等待消息:\x20'+this['waitingMessages']['size']+')'),clearTimeout(this['pollingTimer']),this['pollingTimer']=null,this['scheduleNextPoll']();}['getFailedMessages'](){return Array['from'](this['failedMessages']['entries']())['map'](([a,b])=>({'messageId':a,'from':b['message']['from'],'error':b['error'],'leafIndex':b['message']['merkleMetadata']?.['leafIndex']}));}['clearFailedMessages'](a){a?this['failedMessages']['delete'](a):this['failedMessages']['clear']();}['getWaitingMessageCount'](){return this['waitingMessages']['size'];}}
|
|
1
|
+
import{createHash}from'crypto';import*as a183a from'fs';import*as a183b from'path';import{MessengerSession,DecryptionEngine}from'./session.js';import{MessengerServerClient}from'./server.js';import{MessageStorage,SessionStateStorage}from'./storage.js';import{hashPlaintext,verifyMessage,bytesToBase64}from'./crypto.js';import{canonicalizeJson}from'./utils.js';import{DEFAULT_MESSENGER_CONFIG,MessengerError,MessengerErrorCode,MessageDirection,MessageStatus,WTS_FILE_BYTES_LIMIT,NORMAL_MESSAGE_BYTES_LIMIT,CHAIN_PROOF_TYPE}from'./types.js';import{isValidWowAddress}from'../../utils/sui-types.js';async function getAccount(){const {Account:a}=await import('../local/account.js');return a['Instance']();}export class Messenger{['session'];['serverClient'];['config'];['userAddress']=null;['onMessageCallback']=null;['pollingTimer']=null;['messageConsecutiveEmptyPulls']=0x0;['prekeyConsecutiveOkCount']=0x0;['currentMessageInterval'];['isPollingRunning']=![];['messageStorage'];['sessionStateStorage'];['decryptionEngine'];['waitingMessages']=new Map();['failedMessages']=new Map();['waitingMessageRetries']=new Map();constructor(a,b){this['userAddress']=a,this['config']={...DEFAULT_MESSENGER_CONFIG,...b},this['session']=new MessengerSession(a,this['config']),this['serverClient']=new MessengerServerClient(this['config']),this['messageStorage']=new MessageStorage(a),this['sessionStateStorage']=new SessionStateStorage(a),this['decryptionEngine']=new DecryptionEngine(this['session']['store']),this['currentMessageInterval']=this['config']['message_poll_default_interval_ms']??0x6*0x3c*0x3e8;}['getUserAddress'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');return this['userAddress'];}async['getPublicKey'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await(await getAccount())['get'](this['userAddress'],![]);return a?.['pubkey']||'';}['isValidGuardMessage'](a,b){return Boolean(a&&b&&isValidWowAddress(a)&&isValidWowAddress(b));}async['generateSignatureParams'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b=await(await getAccount())['get'](this['userAddress'],![]);if(!b?.['secret'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20not\x20found\x20or\x20no\x20secret\x20key\x20for\x20'+this['userAddress']);const {Ed25519Keypair:c}=await import('../../keypairs/ed25519/index.js'),d=c['fromSecretKey'](b['secret']),e=d['getPublicKey'](),f=e['toWPublicKey'](),g=Date['now'](),h=this['generateNonce'](),i=a+':'+f+':'+g+':'+h,j=await d['sign'](new TextEncoder()['encode'](i));return{'signatureScheme':'ED25519','signature':Buffer['from'](j)['toString']('base64'),'timestamp':g,'nonce':h};}['generateNonce'](){const a=new Uint8Array(0x10);return crypto['getRandomValues'](a),Array['from'](a,c=>c['toString'](0x10)['padStart'](0x2,'0'))['join']('');}async['initialize'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');await this['session']['ensureIdentity'](this['userAddress']),await this['session']['registerDevice'](this['userAddress']),await this['session']['ensurePreKeys'](this['userAddress']),await this['checkAndRefillPrekeys'](),this['startPollingTimer']();}async['checkAndRefillPrekeys'](){if(!this['userAddress'])return;await this['session']['ensurePreKeys'](this['userAddress'],![]);}['startPollingTimer'](){this['pollingTimer']&&clearTimeout(this['pollingTimer']),this['isPollingRunning']=!![],this['scheduleNextPoll']();}['stopPollingTimer'](){this['isPollingRunning']=![],this['pollingTimer']&&(clearTimeout(this['pollingTimer']),this['pollingTimer']=null);}['scheduleNextPoll'](){if(!this['isPollingRunning'])return;const a=this['getNextPollInterval']();this['pollingTimer']=setTimeout(()=>{this['poll']()['catch'](b=>{})['finally'](()=>{this['scheduleNextPoll']();});},a);}['getNextPollInterval'](){if(this['waitingMessages']['size']>0x0)return this['config']['message_poll_waiting_interval_ms']??0x3*0x3e8;return this['currentMessageInterval'];}async['poll'](){if(!this['userAddress'])return;try{const a=await this['pullMessages']();a['messages']['length']>0x0?(this['messageConsecutiveEmptyPulls']=0x0,this['currentMessageInterval']=this['config']['message_poll_fast_interval_ms']??0x6*0x3e8,this['onMessageCallback']&&this['onMessageCallback'](a['messages'])):(this['messageConsecutiveEmptyPulls']++,this['messageConsecutiveEmptyPulls']>=(this['config']['message_poll_consecutive_empty_limit']??0x3)&&(this['currentMessageInterval']=this['config']['message_poll_default_interval_ms']??0x6*0x3c*0x3e8,this['messageConsecutiveEmptyPulls']=0x0));if(a['prekey_status']){const b=a['prekey_status'];b['shouldRefill']?(this['prekeyConsecutiveOkCount']=0x0,await this['checkAndRefillPrekeys']()):(this['prekeyConsecutiveOkCount']++,this['prekeyConsecutiveOkCount']>=(this['config']['prekey_poll_consecutive_ok_limit']??0x3)&&(this['prekeyConsecutiveOkCount']=0x0));}}catch(c){}}['triggerFastPoll'](){this['currentMessageInterval']=this['config']['message_poll_fast_interval_ms']??0x6*0x3e8,this['messageConsecutiveEmptyPulls']=0x0,this['isPollingRunning']&&(this['pollingTimer']&&clearTimeout(this['pollingTimer']),this['scheduleNextPoll']());}async['sendMessage'](a,b,c){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const d=Date['now'](),e=Date['now'](),f=this['generateNonce'](),g=new TextEncoder()['encode'](b)['length'];if(g>NORMAL_MESSAGE_BYTES_LIMIT)throw new MessengerError(MessengerErrorCode['INVALID_INPUT'],'Message\x20size\x20('+g+'\x20bytes)\x20exceeds\x20maximum\x20allowed\x20size\x20('+NORMAL_MESSAGE_BYTES_LIMIT+'\x20bytes).\x20Consider\x20using\x20sendZipFile\x20for\x20large\x20content.');await this['pullMessages']();const h=this['messageStorage']['getLastReceivedLeafIndex'](this['userAddress'],a),i=await this['session']['encryptMessage'](this['userAddress'],a,b),j=Buffer['from'](new Uint8Array(i['body']))['toString']('base64'),k=hashPlaintext(b,d,c?.['guardAddress'],c?.['passportAddress'],h),l=await(await getAccount())['get'](this['userAddress'],![]);if(!l)throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20account\x20not\x20found');const m=l['pubkey'],n=c?.['guardAddress']||'',o=c?.['passportAddress']||'',p=h>=0x0?h['toString']():'',q=c?.['force']?'true':'false',r='send_message:'+this['userAddress']+':'+a+':'+n+':'+o+':'+k+':'+p+':'+m+':'+e+':'+f+':'+q,s=await(await getAccount())['signData'](this['userAddress'],r),t=Buffer['from'](s['signature']['slice'](0x2),'hex'),u=bytesToBase64(new Uint8Array(t)),v=await this['serverClient']['sendMessage']({'sender':s['publicKey'],'recipient':a,'ciphertext':j,'plaintextHash':k,'clientTimestamp':d,'msgType':i['type'],'signatureScheme':'ED25519','signature':u,'timestamp':e,'nonce':f,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'force':c?.['force'],'lastReceivedLeafIndex':h>=0x0?h:undefined}),w={'messageId':v['messageId'],'fromAddress':this['userAddress'],'toAddress':a,'plaintextHash':k,'plaintext':b,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'lastReceivedLeafIndex':h,'direction':MessageDirection['SENT'],'status':v['status']==='confirmed'?MessageStatus['CONFIRMED']:MessageStatus['PENDING'],'msgType':i['type'],'createdAt':d};v['merkleData']&&(w['leafIndex']=v['merkleData']['leafIndex'],w['prevRoot']=v['merkleData']['prevRoot'],w['newRoot']=v['merkleData']['newRoot'],w['serverSignature']=v['merkleData']['serverSignature'],w['serverTimestamp']=v['merkleData']['serverTimestamp'],w['serverPublicKey']=v['merkleData']['serverPublicKey'],this['sessionStateStorage']['updateSessionState'](this['userAddress'],a,{'currentRoot':v['merkleData']['newRoot'],'prevRoot':v['merkleData']['prevRoot'],'lastLeafIndex':v['merkleData']['leafIndex'],'lastSyncAt':Date['now']()}));this['messageStorage']['saveMessage'](w);if(v['pendingMerkleData'])for(const [x,y]of Object['entries'](v['pendingMerkleData'])){const z=this['messageStorage']['getMessage'](x);z&&z['status']===MessageStatus['PENDING']&&this['messageStorage']['updateMessageStatus'](x,MessageStatus['CONFIRMED'],{'leafIndex':y['leafIndex'],'newRoot':y['newRoot'],'serverSignature':y['serverSignature'],'serverTimestamp':y['serverTimestamp'],'serverPublicKey':y['serverPublicKey']});}return this['triggerFastPoll'](),{'messageId':v['messageId'],'status':w['status'],'merkleData':v['merkleData'],'guardList':v['guard_list'],'lastReceivedLeafIndex':h>=0x0?h:undefined};}async['pullMessages'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b=Date['now'](),c=this['generateNonce'](),d='fetch_messages:'+this['userAddress']+':'+(a||0x64)+':'+b+':'+c,e=await(await getAccount())['get'](this['userAddress'],![]),f=e?.['pubkey']||'',g=await(await getAccount())['signData'](this['userAddress'],d),h=Buffer['from'](g['signature']['slice'](0x2),'hex'),i={'signatureScheme':'ED25519','signature':bytesToBase64(h),'timestamp':b,'nonce':c},j=await this['serverClient']['pullMessages'](this['userAddress'],f,i,a);if(j['messages']['length']===0x0)return{'messages':[],'prekey_status':j['prekey_status']};const k=[],l=[],m=[...j['messages']]['sort'](function(n,o){return(n['clientTimestamp']||0x0)-(o['clientTimestamp']||0x0);});await this['processWaitingMessages'](k,l);for(const n of m){if(this['waitingMessages']['has'](n['id'])){l['push'](n['id']);continue;}if(this['failedMessages']['has'](n['id'])){l['push'](n['id']);continue;}const o=this['messageStorage']['getMessage'](n['id']);if(o){l['push'](n['id']);continue;}const p=await this['tryDecryptMessage'](n);if(p['success']&&p['message']&&p['decryptedData'])this['messageStorage']['saveMessage'](p['message']),k['push'](p['decryptedData']),l['push'](n['id']),this['waitingMessages']['delete'](n['id']);else p['shouldRetry']?this['waitingMessages']['set'](n['id'],n):(this['failedMessages']['set'](n['id'],{'message':n,'error':p['error']||'Unknown\x20error'}),await this['saveFailedMessage'](n,p['error']||'Unknown\x20error'),l['push'](n['id']));}this['waitingMessages']['size']>0x0&&this['isPollingRunning']&&this['reschedulePolling']();if(l['length']>0x0&&this['userAddress']){const q=Date['now'](),r=this['generateNonce'](),s=await(await getAccount())['get'](this['userAddress'],![]);if(s?.['pubkey']){const t='ack_messages:'+this['userAddress']+':'+l['join'](',')+':'+s['pubkey']+':'+q+':'+r,u=await(await getAccount())['signData'](this['userAddress'],t),v=Buffer['from'](u['signature']['slice'](0x2),'hex'),w=bytesToBase64(new Uint8Array(v));await this['serverClient']['acknowledgeMessages'](l,u['publicKey'],{'publicKey':u['publicKey'],'signatureScheme':'ED25519','signature':w,'timestamp':q,'nonce':r});}}return{'messages':k,'prekey_status':j['prekey_status']};}['getLastSentLeafIndex'](a){if(!this['userAddress'])return-0x1;const b=this['messageStorage']['getMessagesBySession'](this['userAddress'],a),c=b['filter'](d=>d['direction']===MessageDirection['SENT']&&d['leafIndex']!==undefined);if(c['length']===0x0)return-0x1;return Math['max'](...c['map'](d=>d['leafIndex']));}['getSessionMessages'](a){if(!this['userAddress'])return[];return this['messageStorage']['getMessagesBySession'](this['userAddress'],a);}['getSessionState'](a){if(!this['userAddress'])return undefined;return this['sessionStateStorage']['getSessionState'](this['userAddress'],a);}async['submitChainProof'](a,b,c){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const d=this['sessionStateStorage']['getSessionState'](this['userAddress'],b);if(!d||!d['currentRoot'])throw new MessengerError(MessengerErrorCode['SESSION_NOT_ESTABLISHED'],'No\x20session\x20state\x20found');const e=this['messageStorage']['getMessagesBySession'](this['userAddress'],b),f=e[e['length']-0x1];if(!f||!f['serverPublicKey']||!f['serverSignature'])throw new MessengerError(MessengerErrorCode['INVALID_PROOF'],'No\x20temporal\x20proof\x20(missing\x20server\x20public\x20key\x20or\x20signature),\x20cannot\x20submit\x20Proof\x20to\x20WoWok\x20blockchain');const g=f['serverPublicKey'],h=f['serverSignature'],i={'sessionId':d['sessionId'],'merkleRoot':d['currentRoot'],'messageCount':d['messageCount'],'timestamp':Date['now']()},{CallProof:j}=await import('../call/proof.js'),k=new j({'proof':JSON['stringify'](i),'server_pubkey':g,'server_signature':h,'proof_type':CHAIN_PROOF_TYPE,'description':c||'Messenger\x20session\x20proof\x20for\x20'+this['userAddress']+'\x20<->\x20'+b,'item_count':d['messageCount'],'about_address':d['sessionId']}),l=await k['call'](a),m=l?.['objectChanges'],n=m?.['find'](o=>o['objectType']?.['includes']('Proof'))?.['objectId'];return{'proofAddress':n||'','txHash':l?.['digest']||''};}['destroy'](){this['stopPollingTimer']();}['disconnect'](){this['destroy']();}['setOnMessageCallback'](a){this['onMessageCallback']=a;}async['sendZipFile'](a,b,c){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const d=await this['loadFileData'](b),{ZipWriter:e,BlobWriter:f,Uint8ArrayReader:g}=await import('@zip.js/zip.js'),h=c?.['fileName']||a183b['basename'](b)||'file.zip',i=c?.['contentType']||this['detectContentType'](h),j=new f('application/zip'),k=new e(j);await k['add'](h,new g(d)),await k['close']();const l=await j['getData'](),m=new Uint8Array(await l['arrayBuffer']());if(m['length']>WTS_FILE_BYTES_LIMIT)throw new MessengerError(MessengerErrorCode['INVALID_INPUT'],'ZIP\x20file\x20size\x20('+m['length']+'\x20bytes)\x20exceeds\x20local\x20maximum\x20allowed\x20size\x20('+WTS_FILE_BYTES_LIMIT+'\x20bytes)');const n='0x'+createHash('sha256')['update'](m)['digest']('hex'),o=Buffer['from'](m)['toString']('base64'),p={'fileName':h+'.zip','fileSize':m['length'],'fileHash':n,'contentType':i};await this['pullMessages']();const q=this['messageStorage']['getLastReceivedLeafIndex'](this['userAddress'],a),r=Date['now'](),s=await this['session']['encryptMessage'](this['userAddress'],a,o),t=Buffer['from'](new Uint8Array(s['body']))['toString']('base64'),u=c?.['guardAddress']||'',v=c?.['passportAddress']||'',w=hashPlaintext(o,r,u,v,q),x=Date['now'](),y=this['generateNonce'](),z=await(await getAccount())['get'](this['userAddress'],![]);if(!z)throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20account\x20not\x20found');const A=z['pubkey'],B=q>=0x0?q['toString']():'',C=c?.['force']?'true':'false',D='send_message:'+this['userAddress']+':'+a+':'+u+':'+v+':'+w+':'+B+':'+A+':'+x+':'+y+':'+C,E=await(await getAccount())['signData'](this['userAddress'],D),F=Buffer['from'](E['signature']['slice'](0x2),'hex'),G=bytesToBase64(new Uint8Array(F)),H=await this['serverClient']['sendMessage']({'sender':E['publicKey'],'recipient':a,'ciphertext':t,'plaintextHash':w,'clientTimestamp':r,'msgType':s['type'],'zipMetadata':p,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'force':c?.['force'],'lastReceivedLeafIndex':q>=0x0?q:undefined,'signatureScheme':'ED25519','signature':G,'timestamp':x,'nonce':y}),I={'messageId':H['messageId'],'fromAddress':this['userAddress'],'toAddress':a,'plaintextHash':w,'plaintext':o,'guardAddress':c?.['guardAddress'],'passportAddress':c?.['passportAddress'],'lastReceivedLeafIndex':q,'direction':MessageDirection['SENT'],'status':H['status']==='confirmed'?MessageStatus['CONFIRMED']:MessageStatus['PENDING'],'msgType':s['type'],'zipMetadata':p,'createdAt':r};H['merkleData']&&(I['leafIndex']=H['merkleData']['leafIndex'],I['prevRoot']=H['merkleData']['prevRoot'],I['newRoot']=H['merkleData']['newRoot'],I['serverSignature']=H['merkleData']['serverSignature'],I['serverTimestamp']=H['merkleData']['serverTimestamp'],I['serverPublicKey']=H['merkleData']['serverPublicKey'],this['sessionStateStorage']['updateSessionState'](this['userAddress'],a,{'currentRoot':H['merkleData']['newRoot'],'prevRoot':H['merkleData']['prevRoot'],'lastLeafIndex':H['merkleData']['leafIndex'],'lastSyncAt':Date['now']()}));this['messageStorage']['saveMessage'](I);if(H['pendingMerkleData'])for(const [J,K]of Object['entries'](H['pendingMerkleData'])){const L=this['messageStorage']['getMessage'](J);L&&L['status']===MessageStatus['PENDING']&&this['messageStorage']['updateMessageStatus'](J,MessageStatus['CONFIRMED'],{'leafIndex':K['leafIndex'],'newRoot':K['newRoot'],'serverSignature':K['serverSignature'],'serverTimestamp':K['serverTimestamp'],'serverPublicKey':K['serverPublicKey']});}return this['triggerFastPoll'](),{'messageId':H['messageId'],'status':I['status'],'merkleData':H['merkleData'],'guardList':H['guard_list'],'lastReceivedLeafIndex':q>=0x0?q:undefined};}async['loadFileData'](a){if(a['startsWith']('http://')||a['startsWith']('https://')){const d=await fetch(a);if(!d['ok'])throw new MessengerError(MessengerErrorCode['NETWORK_ERROR'],'Failed\x20to\x20fetch\x20file\x20from\x20'+a+':\x20'+d['statusText']);const e=await d['arrayBuffer']();return new Uint8Array(e);}const b=a183b['resolve'](a);if(!a183a['existsSync'](b))throw new MessengerError(MessengerErrorCode['FILE_NOT_FOUND'],'File\x20not\x20found:\x20'+b);const c=a183a['readFileSync'](b);return new Uint8Array(c);}['detectContentType'](a){const b=a183b['extname'](a)['toLowerCase']();switch(b){case'.wts':return'wts';case'.wip':return'wip';default:return'zip';}}async['_createSignedRequest'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b=Date['now'](),c=this['generateNonce'](),d=canonicalizeJson(a),e=b+':'+c+':'+d,f=await(await getAccount())['signData'](this['userAddress'],e),g=Buffer['from'](f['signature']['slice'](0x2),'hex'),h=bytesToBase64(new Uint8Array(g)),i=await(await getAccount())['get'](this['userAddress'],![]);if(!i||!i['pubkey'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20public\x20key\x20not\x20found');return{'userAddress':this['userAddress'],'publicKey':i['pubkey'],'signatureScheme':'ED25519','signature':h,'timestamp':b,'nonce':c,'data':a};}['_validateAddresses'](a){const b=[],c=[];for(const d of a){isValidWowAddress(d)?b['push'](d):c['push'](d);}return{'valid':b,'invalid':c};}async['addToBlacklist'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'add','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['addToBlacklist'](this['userAddress'],e);}async['removeFromBlacklist'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'remove','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['removeFromBlacklist'](this['userAddress'],e);}async['clearBlacklist'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a={},b=await this['_createSignedRequest'](a);return this['serverClient']['clearBlacklist'](this['userAddress'],b);}async['getBlacklist'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getBlacklist'](this['userAddress'],a);}async['existInBlacklist'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'exist','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['existInBlacklist'](this['userAddress'],e);}async['addToFriendsList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'add','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['addToFriendsList'](this['userAddress'],e);}async['removeFromFriendsList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'remove','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['removeFromFriendsList'](this['userAddress'],e);}async['clearFriendsList'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a={},b=await this['_createSignedRequest'](a);return this['serverClient']['clearFriendsList'](this['userAddress'],b);}async['getFriendsList'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getFriendsList'](this['userAddress'],a);}async['existInFriendsList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'exist','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['existInFriendsList'](this['userAddress'],e);}async['addToGuardList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b={'guards':a},c=await this['_createSignedRequest'](b);return this['serverClient']['addToGuardList'](this['userAddress'],c);}async['removeFromGuardList'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const {valid:b,invalid:c}=this['_validateAddresses'](a);if(c['length']>0x0)return{'success':![],'operation':'remove','modifiedCount':0x0,'currentCount':0x0,'maxCount':0x0,'invalidAddresses':c,'message':'Invalid\x20addresses:\x20'+c['join'](',\x20')};const d={'addresses':b},e=await this['_createSignedRequest'](d);return this['serverClient']['removeFromGuardList'](this['userAddress'],e);}async['getGuardList'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getGuardList'](this['userAddress'],a);}async['getSettings'](){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const a=await this['_createSignedRequest']({});return this['serverClient']['getSettings'](this['userAddress'],a);}async['setSettings'](a){if(!this['userAddress'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'User\x20address\x20not\x20set');const b={'allowStrangerMessages':a['allowStrangerMessages'],'maxInboxSize':a['maxInboxSize']},c=await this['_createSignedRequest'](b);return this['serverClient']['updateSettings'](this['userAddress'],c);}async['tryDecryptMessage'](a){try{const b=Uint8Array['from'](Buffer['from'](a['bodyB64'],'base64')),c=await this['decryptionEngine']['decryptMessage'](this['userAddress'],a['from'],b['buffer'],a['msgType']);if(!c['success']){const h=this['isRetryableError'](c['error']||'');return{'success':![],'shouldRetry':h,'error':c['error'],'sessionUpdated':c['sessionUpdated']};}const d=c['plaintext'];if(!a['clientTimestamp'])throw new Error('Missing\x20clientTimestamp\x20for\x20message\x20'+a['id']);if(!a['plaintextHash'])throw new Error('Missing\x20plaintextHash\x20for\x20message\x20'+a['id']);const e=verifyMessage({'messageId':a['id'],'plaintext':d,'plaintextHash':a['plaintextHash'],'createdAt':a['clientTimestamp'],'guardAddress':a['guardAddress'],'passportAddress':a['passportAddress'],'lastReceivedLeafIndex':a['lastReceivedLeafIndex'],'serverSignature':a['merkleMetadata']?.['serverSignature'],'serverPublicKey':a['merkleMetadata']?.['serverPublicKey'],'merkleMetadata':a['merkleMetadata']?{'prevRoot':a['merkleMetadata']['prevRoot'],'newRoot':a['merkleMetadata']['newRoot'],'serverTimestamp':a['merkleMetadata']['serverTimestamp'],'leafIndex':a['merkleMetadata']['leafIndex'],'proofSiblings':a['merkleMetadata']['proofSiblings'],'proofIndices':a['merkleMetadata']['proofIndices']}:undefined});if(!e['valid'])throw new Error(e['error']);const f={'messageId':a['id'],'fromAddress':a['from'],'toAddress':this['userAddress'],'plaintextHash':a['plaintextHash'],'plaintext':d,'guardAddress':a['guardAddress'],'passportAddress':a['passportAddress'],'lastReceivedLeafIndex':a['lastReceivedLeafIndex'],'direction':MessageDirection['RECEIVED'],'status':MessageStatus['DECRYPTED'],'msgType':a['msgType'],'leafIndex':a['merkleMetadata']?.['leafIndex'],'prevRoot':a['merkleMetadata']?.['prevRoot'],'newRoot':a['merkleMetadata']?.['newRoot'],'serverSignature':a['merkleMetadata']?.['serverSignature'],'serverTimestamp':a['merkleMetadata']?.['serverTimestamp'],'serverPublicKey':a['merkleMetadata']?.['serverPublicKey'],'createdAt':a['clientTimestamp'],'receivedAt':Date['now'](),'zipMetadata':a['zipMetadata']};a['merkleMetadata']&&this['sessionStateStorage']['updateSessionState'](this['userAddress'],a['from'],{'currentRoot':a['merkleMetadata']['newRoot'],'prevRoot':a['merkleMetadata']['prevRoot'],'lastLeafIndex':a['merkleMetadata']['leafIndex'],'lastSyncAt':Date['now']()});const g=a['merkleMetadata']?.['serverTimestamp']||a['clientTimestamp']||Date['now']();if(a['lastReceivedLeafIndex']!==undefined&&a['lastReceivedLeafIndex']>=0x0){const i=this['messageStorage']['getMessagesBySession'](this['userAddress'],a['from'])['filter'](j=>j['direction']===MessageDirection['SENT']&&j['status']===MessageStatus['CONFIRMED']&&j['leafIndex']!==undefined&&j['leafIndex']<=a['lastReceivedLeafIndex']);for(const j of i){this['messageStorage']['updateMessageStatus'](j['messageId'],MessageStatus['READ']);}}return{'success':!![],'shouldRetry':![],'message':f,'decryptedData':{'id':a['id'],'from':a['from'],'plaintext':d,'timestamp':g,'merkleVerified':!!a['merkleMetadata'],'merkleData':a['merkleMetadata']?{'leafIndex':a['merkleMetadata']['leafIndex'],'rootHash':a['merkleMetadata']['newRoot']}:undefined},'sessionUpdated':c['sessionUpdated']};}catch(k){const l=k instanceof Error?k['message']:String(k),m=this['isRetryableError'](l);return{'success':![],'shouldRetry':m,'error':l};}}['isRetryableError'](a){const b=[/message number/i,/chain key/i,/session not found/i,/prekey not found/i,/PREKEY 竞争:我的地址较小,保留我的会话/i,/The operation failed for an operation-specific reason/i,/DOMException/i,/收到 WHISPER_MESSAGE 但无现有会话,需要等待 PREKEY_MESSAGE/i];return b['some'](c=>c['test'](a));}async['processWaitingMessages'](a,b){if(this['waitingMessages']['size']===0x0)return;const c=[],d=Array['from'](this['waitingMessages']['values']())['sort'](function(e,f){return(e['clientTimestamp']||0x0)-(f['clientTimestamp']||0x0);});for(const e of d){const f=e['id'],g=await this['tryDecryptMessage'](e);if(g['success']&&g['message']&&g['decryptedData'])this['messageStorage']['saveMessage'](g['message']),a['push'](g['decryptedData']),b['push'](f),c['push'](f),this['waitingMessageRetries']['delete'](f);else{if(!g['shouldRetry'])this['failedMessages']['set'](f,{'message':e,'error':g['error']||'Unknown\x20error'}),await this['saveFailedMessage'](e,g['error']||'Unknown\x20error'),b['push'](f),c['push'](f),this['waitingMessageRetries']['delete'](f);else{const h=this['waitingMessageRetries']['get'](f)||0x0,i=h+0x1;i>=0x3?(this['failedMessages']['set'](f,{'message':e,'error':g['error']||'Retry\x20limit\x20exceeded'}),await this['saveFailedMessage'](e,g['error']||'Retry\x20limit\x20exceeded'),b['push'](f),c['push'](f),this['waitingMessageRetries']['delete'](f)):this['waitingMessageRetries']['set'](f,i);}}}for(const j of c){this['waitingMessages']['delete'](j);}if(c['length']>0x0){}}async['saveFailedMessage'](a,b){const c={'messageId':a['id'],'fromAddress':a['from'],'toAddress':this['userAddress'],'plaintextHash':a['plaintextHash']||'','guardAddress':a['guardAddress'],'passportAddress':a['passportAddress'],'lastReceivedLeafIndex':a['lastReceivedLeafIndex'],'direction':MessageDirection['RECEIVED'],'status':MessageStatus['DECRYPT_FAILED'],'msgType':a['msgType'],'leafIndex':a['merkleMetadata']?.['leafIndex'],'prevRoot':a['merkleMetadata']?.['prevRoot'],'newRoot':a['merkleMetadata']?.['newRoot'],'serverSignature':a['merkleMetadata']?.['serverSignature'],'serverTimestamp':a['merkleMetadata']?.['serverTimestamp'],'serverPublicKey':a['merkleMetadata']?.['serverPublicKey'],'createdAt':a['clientTimestamp']||Date['now'](),'receivedAt':Date['now'](),'zipMetadata':a['zipMetadata'],'decryptError':b,'decryptAttempts':0x1,'lastDecryptAttemptAt':Date['now']()};this['messageStorage']['saveMessage'](c);}['reschedulePolling'](){if(!this['isPollingRunning']||!this['pollingTimer'])return;clearTimeout(this['pollingTimer']),this['pollingTimer']=null,this['scheduleNextPoll']();}['getFailedMessages'](){return Array['from'](this['failedMessages']['entries']())['map'](([a,b])=>({'messageId':a,'from':b['message']['from'],'error':b['error'],'leafIndex':b['message']['merkleMetadata']?.['leafIndex']}));}['clearFailedMessages'](a){a?this['failedMessages']['delete'](a):this['failedMessages']['clear']();}['getWaitingMessageCount'](){return this['waitingMessages']['size'];}}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { X25519KeyPair, Ed25519KeyPair, MLKEMKeyPair } from "./types.js";
|
|
2
|
+
export declare function generateRandomBytes(length: number): Uint8Array;
|
|
3
|
+
export declare function generateX25519KeyPair(): X25519KeyPair;
|
|
4
|
+
export declare function x25519DH(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
|
|
5
|
+
export declare function generateEd25519KeyPair(): Ed25519KeyPair;
|
|
6
|
+
export declare function ed25519Sign(privateKey: Uint8Array, message: Uint8Array): Uint8Array;
|
|
7
|
+
export declare function ed25519Verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): boolean;
|
|
8
|
+
export declare function ed25519PrivateKeyToX25519(ed25519PrivateKey: Uint8Array): Uint8Array;
|
|
9
|
+
export declare function ed25519PublicKeyToX25519(ed25519PublicKey: Uint8Array): Uint8Array;
|
|
10
|
+
export declare function generateMLKEMKeyPair(): MLKEMKeyPair;
|
|
11
|
+
export declare function mlkemEncapsulate(publicKey: Uint8Array): {
|
|
12
|
+
ciphertext: Uint8Array;
|
|
13
|
+
sharedSecret: Uint8Array;
|
|
14
|
+
};
|
|
15
|
+
export declare function mlkemDecapsulate(ciphertext: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
16
|
+
export declare function hkdfDerive(inputKeyMaterial: Uint8Array, info: Uint8Array, length: number, salt?: Uint8Array): Uint8Array;
|
|
17
|
+
export declare function hkdfChainKey(chainKey: Uint8Array, dhOutput: Uint8Array): {
|
|
18
|
+
rootKey: Uint8Array;
|
|
19
|
+
chainKey: Uint8Array;
|
|
20
|
+
};
|
|
21
|
+
export declare function hkdfMessageKey(chainKey: Uint8Array): {
|
|
22
|
+
messageKey: Uint8Array;
|
|
23
|
+
nextChainKey: Uint8Array;
|
|
24
|
+
};
|
|
25
|
+
export declare function hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;
|
|
26
|
+
export declare function aesGcmEncrypt(key: Uint8Array, plaintext: Uint8Array, associatedData: Uint8Array): {
|
|
27
|
+
ciphertext: Uint8Array;
|
|
28
|
+
iv: Uint8Array;
|
|
29
|
+
tag: Uint8Array;
|
|
30
|
+
};
|
|
31
|
+
export declare function aesGcmDecrypt(key: Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array, associatedData: Uint8Array): Uint8Array;
|
|
32
|
+
export declare function sha256Hash(data: Uint8Array): Uint8Array;
|
|
33
|
+
export declare function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
34
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
35
|
+
export declare function base64ToBytes(base64: string): Uint8Array;
|
|
36
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
37
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
38
|
+
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
|
|
39
|
+
export declare function deriveRegistrationId(publicKey: Uint8Array): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{x25519}from'@noble/curves/ed25519';import{ed25519}from'@noble/curves/ed25519';import{sha256}from'@noble/hashes/sha256';import{sha512}from'@noble/hashes/sha512';import{hmac}from'@noble/hashes/hmac';import{hkdf}from'@noble/hashes/hkdf';import{randomBytes}from'@noble/hashes/utils';import{ml_kem768}from'@noble/post-quantum/ml-kem.js';import a184a from'node:crypto';import{ROOT_KEY_SIZE,CHAIN_KEY_SIZE,MESSAGE_KEY_SIZE,DH_KEY_SIZE,AES_GCM_IV_SIZE}from'./types.js';export function generateRandomBytes(a){return randomBytes(a);}export function generateX25519KeyPair(){const a=randomBytes(DH_KEY_SIZE),b=x25519['getPublicKey'](a);return{'publicKey':b,'privateKey':a};}export function x25519DH(a,b){return x25519['getSharedSecret'](a,b);}export function generateEd25519KeyPair(){const a=randomBytes(DH_KEY_SIZE),b=ed25519['getPublicKey'](a);return{'publicKey':b,'privateKey':a};}export function ed25519Sign(a,b){return ed25519['sign'](b,a);}export function ed25519Verify(a,b,c){try{return ed25519['verify'](c,b,a);}catch{return![];}}export function ed25519PrivateKeyToX25519(a){const b=sha512(a),c=new Uint8Array(b['slice'](0x0,0x20));return c[0x0]&=0xf8,c[0x1f]&=0x7f,c[0x1f]|=0x40,c;}export function ed25519PublicKeyToX25519(a){return x25519['getPublicKey'](ed25519PublicKeyToX25519(a));}export function generateMLKEMKeyPair(){const {publicKey:a,secretKey:b}=ml_kem768['keygen']();return{'publicKey':a,'secretKey':b};}export function mlkemEncapsulate(a){const {cipherText:b,sharedSecret:c}=ml_kem768['encapsulate'](a);return{'ciphertext':b,'sharedSecret':c};}export function mlkemDecapsulate(a,b){return ml_kem768['decapsulate'](a,b);}export function hkdfDerive(a,b,c,d){return hkdf(sha256,a,d||new Uint8Array(0x0),b,c);}export function hkdfChainKey(a,b){const c=new Uint8Array(a['length']+b['length']);c['set'](a),c['set'](b,a['length']);const d=hkdfDerive(c,new TextEncoder()['encode']('WowokPQChainKey'),ROOT_KEY_SIZE+CHAIN_KEY_SIZE);return{'rootKey':d['slice'](0x0,ROOT_KEY_SIZE),'chainKey':d['slice'](ROOT_KEY_SIZE,ROOT_KEY_SIZE+CHAIN_KEY_SIZE)};}export function hkdfMessageKey(a){const b=hkdfDerive(a,new TextEncoder()['encode']('WowokPQMessageKey'),MESSAGE_KEY_SIZE+CHAIN_KEY_SIZE);return{'messageKey':b['slice'](0x0,MESSAGE_KEY_SIZE),'nextChainKey':b['slice'](MESSAGE_KEY_SIZE,MESSAGE_KEY_SIZE+CHAIN_KEY_SIZE)};}export function hmacSha256(a,b){return hmac(sha256,a,b);}export function aesGcmEncrypt(a,b,c){const d=randomBytes(AES_GCM_IV_SIZE),e=Buffer['from'](a),f=Buffer['from'](d),g=Buffer['from'](b),h=Buffer['from'](c),i=a184a['createCipheriv']('aes-256-gcm',e,f);i['setAAD'](h);const j=Buffer['concat']([i['update'](g),i['final']()]),k=i['getAuthTag']();return{'ciphertext':new Uint8Array(j),'iv':d,'tag':new Uint8Array(k)};}export function aesGcmDecrypt(a,b,c,d,e){const f=Buffer['from'](a),g=Buffer['from'](c),h=Buffer['from'](b),i=Buffer['from'](d),j=Buffer['from'](e),k=a184a['createDecipheriv']('aes-256-gcm',f,g);k['setAAD'](j),k['setAuthTag'](i);const l=Buffer['concat']([k['update'](h),k['final']()]);return new Uint8Array(l);}export function sha256Hash(a){return sha256(a);}export function constantTimeEqual(c,d){if(c['length']!==d['length'])return![];let e=0x0;for(let f=0x0;f<c['length'];f++){e|=c[f]^d[f];}return e===0x0;}export function bytesToBase64(a){return Buffer['from'](a)['toString']('base64');}export function base64ToBytes(a){return new Uint8Array(Buffer['from'](a,'base64'));}export function bytesToHex(a){return Buffer['from'](a)['toString']('hex');}export function hexToBytes(a){return new Uint8Array(Buffer['from'](a,'hex'));}export function concatBytes(...a){const b=a['reduce']((e,f)=>e+f['length'],0x0),c=new Uint8Array(b);let d=0x0;for(const e of a){c['set'](e,d),d+=e['length'];}return c;}export function deriveRegistrationId(a){const b=(a[0x0]<<0x8|a[0x1])>>>0x0;return b===0x0?0x1:b;}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const DATA_SHARDS = 28;
|
|
2
|
+
declare const PARITY_SHARDS = 28;
|
|
3
|
+
declare const TOTAL_SHARDS = 56;
|
|
4
|
+
declare const SHARD_DATA_SIZE = 44;
|
|
5
|
+
declare const SHARD_HEADER_SIZE = 8;
|
|
6
|
+
declare const SHARD_SIZE: number;
|
|
7
|
+
export declare function encodeShards(data: Uint8Array): Uint8Array[];
|
|
8
|
+
export declare function decodeShards(availableShards: Map<number, Uint8Array>, totalShards: number): Uint8Array;
|
|
9
|
+
export interface SPQRChunkData {
|
|
10
|
+
cycleId: number;
|
|
11
|
+
chunkIndex: number;
|
|
12
|
+
totalChunks: number;
|
|
13
|
+
data: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
export declare function packChunk(chunk: SPQRChunkData): Uint8Array;
|
|
16
|
+
export declare function unpackChunk(packed: Uint8Array): SPQRChunkData;
|
|
17
|
+
export { DATA_SHARDS, PARITY_SHARDS, TOTAL_SHARDS, SHARD_DATA_SIZE, SHARD_HEADER_SIZE, SHARD_SIZE, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{PQError,PQErrorCode}from'./types.js';const DATA_SHARDS=0x1c,PARITY_SHARDS=0x1c,TOTAL_SHARDS=0x38,SHARD_DATA_SIZE=0x2c,SHARD_HEADER_SIZE=0x8,SHARD_SIZE=SHARD_DATA_SIZE+SHARD_HEADER_SIZE,EXP_TABLE=new Array(0x200),LOG_TABLE=new Array(0x100);function initGFTables(){if(EXP_TABLE[0x0]!==undefined)return;let a=0x1;for(let b=0x0;b<0xff;b++){EXP_TABLE[b]=a,EXP_TABLE[b+0xff]=a,LOG_TABLE[a]=b,a<<=0x1,a&0x100&&(a^=0x11d);}LOG_TABLE[0x0]=0x0;}function gfMul(c,d){if(c===0x0||d===0x0)return 0x0;return initGFTables(),EXP_TABLE[(LOG_TABLE[c]+LOG_TABLE[d])%0xff];}function gfDiv(c,d){if(c===0x0)return 0x0;if(d===0x0)throw new PQError(PQErrorCode['ERASURE_ERROR'],'Division\x20by\x20zero');initGFTables();const e=(LOG_TABLE[c]-LOG_TABLE[d]+0xff)%0xff;return EXP_TABLE[e];}function buildVandermondeMatrix(a,b,c=0x0){const d=[];for(let e=0x0;e<a;e++){d[e]=[];for(let f=0x0;f<b;f++){d[e][f]=gfPow(c+e,f);}}return d;}function buildEncodingMatrix(a,b){const c=[];for(let f=0x0;f<b;f++){c[f]=new Array(b)['fill'](0x0),c[f][f]=0x1;}const d=a-b,e=buildVandermondeMatrix(d,b,b);for(let g=0x0;g<d;g++){c[b+g]=e[g];}return c;}function gfPow(a,b){if(b===0x0)return 0x1;if(a===0x0)return 0x0;let c=0x1;for(let d=0x0;d<b;d++){c=gfMul(c,a);}return c;}function matrixInvert(a){const b=a['length'],c=[];for(let e=0x0;e<b;e++){c[e]=[...a[e]];for(let f=0x0;f<b;f++){c[e]['push'](e===f?0x1:0x0);}}for(let g=0x0;g<b;g++){let h=g;while(h<b&&c[h][g]===0x0){h++;}if(h===b)throw new PQError(PQErrorCode['ERASURE_ERROR'],'Matrix\x20is\x20singular');h!==g&&([c[g],c[h]]=[c[h],c[g]]);const k=gfDiv(0x1,c[g][g]);for(let l=0x0;l<0x2*b;l++){c[g][l]=gfMul(c[g][l],k);}for(let m=0x0;m<b;m++){if(m!==g){const o=c[m][g];if(o!==0x0)for(let p=0x0;p<0x2*b;p++){c[m][p]^=gfMul(o,c[g][p]);}}}}const d=[];for(let q=0x0;q<b;q++){d[q]=c[q]['slice'](b);}return d;}export function encodeShards(a){const b=DATA_SHARDS*SHARD_DATA_SIZE;if(a['length']>b)throw new PQError(PQErrorCode['ERASURE_ERROR'],'Data\x20too\x20large:\x20'+a['length']+'\x20>\x20'+b);const c=new Uint8Array(b);c['set'](a);const d=[];for(let f=0x0;f<DATA_SHARDS;f++){d[f]=c['slice'](f*SHARD_DATA_SIZE,(f+0x1)*SHARD_DATA_SIZE);}const e=buildVandermondeMatrix(PARITY_SHARDS,DATA_SHARDS,DATA_SHARDS);for(let g=0x0;g<PARITY_SHARDS;g++){const h=new Uint8Array(SHARD_DATA_SIZE);for(let l=0x0;l<SHARD_DATA_SIZE;l++){let m=0x0;for(let n=0x0;n<DATA_SHARDS;n++){m^=gfMul(e[g][n],d[n][l]);}h[l]=m;}d[DATA_SHARDS+g]=h;}return d;}export function decodeShards(a,b){const c=Array['from'](a['keys']())['sort']((l,m)=>l-m);if(c['length']<DATA_SHARDS)throw new PQError(PQErrorCode['ERASURE_ERROR'],'Need\x20at\x20least\x20'+DATA_SHARDS+'\x20shards,\x20got\x20'+c['length']);const d=c['slice'](0x0,DATA_SHARDS),e=buildEncodingMatrix(b,DATA_SHARDS),f=[];for(const l of d){f['push'](e[l]);}const g=matrixInvert(f),h=new Uint8Array(DATA_SHARDS*SHARD_DATA_SIZE);for(let m=0x0;m<DATA_SHARDS;m++){for(let n=0x0;n<SHARD_DATA_SIZE;n++){let o=0x0;for(let p=0x0;p<DATA_SHARDS;p++){const q=a['get'](d[p]);o^=gfMul(g[m][p],q[n]);}h[m*SHARD_DATA_SIZE+n]=o;}}return h;}export function packChunk(a){const b=new Uint8Array(SHARD_SIZE),c=new DataView(b['buffer']);return c['setUint32'](0x0,a['cycleId'],![]),c['setUint16'](0x4,a['chunkIndex'],![]),c['setUint16'](0x6,a['totalChunks'],![]),b['set'](a['data']['slice'](0x0,SHARD_DATA_SIZE),SHARD_HEADER_SIZE),b;}export function unpackChunk(a){if(a['length']<SHARD_SIZE)throw new PQError(PQErrorCode['ERASURE_ERROR'],'Chunk\x20too\x20small:\x20'+a['length']+'\x20<\x20'+SHARD_SIZE);const b=new DataView(a['buffer'],a['byteOffset'],a['byteLength']),c=b['getUint32'](0x0,![]),d=b['getUint16'](0x4,![]),e=b['getUint16'](0x6,![]),f=a['slice'](SHARD_HEADER_SIZE,SHARD_HEADER_SIZE+SHARD_DATA_SIZE);return{'cycleId':c,'chunkIndex':d,'totalChunks':e,'data':f};}export{DATA_SHARDS,PARITY_SHARDS,TOTAL_SHARDS,SHARD_DATA_SIZE,SHARD_HEADER_SIZE,SHARD_SIZE};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./crypto.js";
|
|
3
|
+
export * from "./pqxdh.js";
|
|
4
|
+
export * from "./ratchet.js";
|
|
5
|
+
export * from "./erasure.js";
|
|
6
|
+
export * from "./spqr.js";
|
|
7
|
+
export * from "./keycombiner.js";
|
|
8
|
+
export * from "./session.js";
|
|
9
|
+
export * from "./store-adapter.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from'./types.js';export*from'./crypto.js';export*from'./pqxdh.js';export*from'./ratchet.js';export*from'./erasure.js';export*from'./spqr.js';export*from'./keycombiner.js';export*from'./session.js';export*from'./store-adapter.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function combineKeys(dhMessageKey: Uint8Array, spqrKey: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{hkdfDerive,concatBytes}from'./crypto.js';import{MESSAGE_KEY_SIZE}from'./types.js';export function combineKeys(a,b){return hkdfDerive(concatBytes(a,b),new TextEncoder()['encode']('WowokPQCombinedKey'),MESSAGE_KEY_SIZE);}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { X25519KeyPair, MLKEMKeyPair, PQPreKeyBundle, PQXDHResult } from "./types.js";
|
|
2
|
+
export interface PQXDHInitiateResult {
|
|
3
|
+
result: PQXDHResult;
|
|
4
|
+
initialMessage: {
|
|
5
|
+
ephemeralPublicKey: Uint8Array;
|
|
6
|
+
mlkemCiphertext: Uint8Array;
|
|
7
|
+
identityKey: Uint8Array;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare function pqxdhInitiate(ourIdentityKeyPair: X25519KeyPair, ourEd25519KeyPair: {
|
|
11
|
+
publicKey: Uint8Array;
|
|
12
|
+
privateKey: Uint8Array;
|
|
13
|
+
}, peerBundle: PQPreKeyBundle): PQXDHInitiateResult;
|
|
14
|
+
export declare function pqxdhReceive(ourIdentityKeyPair: X25519KeyPair, ourSignedPreKeyPair: X25519KeyPair, ourOneTimePreKeyPair: X25519KeyPair | null, ourPQPreKeyPair: MLKEMKeyPair, peerEphemeralPublicKey: Uint8Array, peerIdentityKey: Uint8Array, mlkemCiphertext: Uint8Array): PQXDHResult;
|
|
15
|
+
export interface GeneratedPreKeyBundle {
|
|
16
|
+
identityKeyPair: X25519KeyPair;
|
|
17
|
+
ed25519KeyPair: {
|
|
18
|
+
publicKey: Uint8Array;
|
|
19
|
+
privateKey: Uint8Array;
|
|
20
|
+
};
|
|
21
|
+
registrationId: number;
|
|
22
|
+
signedPreKey: {
|
|
23
|
+
keyId: number;
|
|
24
|
+
keyPair: X25519KeyPair;
|
|
25
|
+
signature: Uint8Array;
|
|
26
|
+
};
|
|
27
|
+
oneTimePreKeys: Array<{
|
|
28
|
+
keyId: number;
|
|
29
|
+
keyPair: X25519KeyPair;
|
|
30
|
+
}>;
|
|
31
|
+
pqPreKey: {
|
|
32
|
+
keyId: number;
|
|
33
|
+
keyPair: MLKEMKeyPair;
|
|
34
|
+
signature: Uint8Array;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare function generatePreKeyBundle(identityKeyPair: X25519KeyPair, ed25519KeyPair: {
|
|
38
|
+
publicKey: Uint8Array;
|
|
39
|
+
privateKey: Uint8Array;
|
|
40
|
+
}, signedPreKeyId: number, pqPreKeyId: number, oneTimePreKeyCount: number): GeneratedPreKeyBundle;
|
|
41
|
+
export declare function verifyPreKeyBundle(bundle: PQPreKeyBundle): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{generateX25519KeyPair,x25519DH,ed25519Sign,ed25519Verify,generateMLKEMKeyPair,mlkemEncapsulate,mlkemDecapsulate,hkdfDerive,concatBytes,deriveRegistrationId}from'./crypto.js';import{ROOT_KEY_SIZE,CHAIN_KEY_SIZE}from'./types.js';export function pqxdhInitiate(a,b,c){const d=generateX25519KeyPair(),e=x25519DH(d['privateKey'],c['identityKey']),f=x25519DH(a['privateKey'],c['signedPreKey']['publicKey']),g=x25519DH(d['privateKey'],c['signedPreKey']['publicKey']),{ciphertext:h,sharedSecret:i}=mlkemEncapsulate(c['pqPreKey']['publicKey']);let j;if(c['oneTimePreKey']){const n=x25519DH(d['privateKey'],c['oneTimePreKey']['publicKey']);j=concatBytes(e,f,g,n,i);}else j=concatBytes(e,f,g,i);const k=concatBytes(new TextEncoder()['encode']('WowokPQXDH'),a['publicKey'],c['identityKey']),l=hkdfDerive(j,k,ROOT_KEY_SIZE+CHAIN_KEY_SIZE),m={'rootKey':l['slice'](0x0,ROOT_KEY_SIZE),'chainKey':l['slice'](ROOT_KEY_SIZE,ROOT_KEY_SIZE+CHAIN_KEY_SIZE),'ourDHKeyPair':d,'peerDHPublicKey':c['signedPreKey']['publicKey'],'peerIdentityKey':c['identityKey'],'pqSharedSecret':i};return{'result':m,'initialMessage':{'ephemeralPublicKey':d['publicKey'],'mlkemCiphertext':h,'identityKey':a['publicKey']}};}export function pqxdhReceive(a,b,c,d,e,f,g){const h=x25519DH(a['privateKey'],e),i=x25519DH(b['privateKey'],f),j=x25519DH(b['privateKey'],e),k=mlkemDecapsulate(g,d['secretKey']);let l;if(c){const o=x25519DH(c['privateKey'],e);l=concatBytes(h,i,j,o,k);}else l=concatBytes(h,i,j,k);const m=concatBytes(new TextEncoder()['encode']('WowokPQXDH'),f,a['publicKey']),n=hkdfDerive(l,m,ROOT_KEY_SIZE+CHAIN_KEY_SIZE);return{'rootKey':n['slice'](0x0,ROOT_KEY_SIZE),'chainKey':n['slice'](ROOT_KEY_SIZE,ROOT_KEY_SIZE+CHAIN_KEY_SIZE),'ourDHKeyPair':b,'peerDHPublicKey':e,'peerIdentityKey':f,'pqSharedSecret':k};}export function generatePreKeyBundle(a,b,c,d,e){const f=deriveRegistrationId(a['publicKey']),g=generateX25519KeyPair(),h=ed25519Sign(b['privateKey'],g['publicKey']),j=[];for(let m=0x0;m<e;m++){j['push']({'keyId':m+0x1,'keyPair':generateX25519KeyPair()});}const k=generateMLKEMKeyPair(),l=ed25519Sign(b['privateKey'],k['publicKey']);return{'identityKeyPair':a,'ed25519KeyPair':b,'registrationId':f,'signedPreKey':{'keyId':c,'keyPair':g,'signature':h},'oneTimePreKeys':j,'pqPreKey':{'keyId':d,'keyPair':k,'signature':l}};}export function verifyPreKeyBundle(a){if(!a['ed25519PublicKey']||a['ed25519PublicKey']['length']!==0x20)return![];if(!a['signedPreKey'])return![];const b=ed25519Verify(a['ed25519PublicKey'],a['signedPreKey']['publicKey'],a['signedPreKey']['signature']);if(!b)return![];if(!a['pqPreKey'])return![];const c=ed25519Verify(a['ed25519PublicKey'],a['pqPreKey']['publicKey'],a['pqPreKey']['signature']);if(!c)return![];return!![];}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RatchetState, X25519KeyPair, EncryptedMessage, SerializedRatchetState } from "./types.js";
|
|
2
|
+
export declare function initRatchet(rootKey: Uint8Array, sendingChainKey: Uint8Array, receivingChainKey: Uint8Array, ourDHKeyPair: X25519KeyPair, peerDHPublicKey: Uint8Array): RatchetState;
|
|
3
|
+
export declare function ratchetEncrypt(state: RatchetState, plaintext: Uint8Array): {
|
|
4
|
+
message: EncryptedMessage;
|
|
5
|
+
newState: RatchetState;
|
|
6
|
+
};
|
|
7
|
+
export declare function ratchetDecrypt(state: RatchetState, messageBody: Uint8Array): {
|
|
8
|
+
plaintext: Uint8Array;
|
|
9
|
+
newState: RatchetState;
|
|
10
|
+
};
|
|
11
|
+
export declare function serializeRatchetState(state: RatchetState): SerializedRatchetState;
|
|
12
|
+
export declare function deserializeRatchetState(serialized: SerializedRatchetState): RatchetState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{generateX25519KeyPair,x25519DH,hkdfChainKey,hkdfMessageKey,aesGcmEncrypt,aesGcmDecrypt,concatBytes}from'./crypto.js';import{MessageType,PQError,PQErrorCode}from'./types.js';function encodeHeader(a){const b=new Uint8Array(0x20+0x4+0x4);b['set'](a['dhPublicKey'],0x0);const c=new DataView(b['buffer']);return c['setUint32'](0x20,a['previousSendingCount'],![]),c['setUint32'](0x24,a['messageCount'],![]),b;}function decodeHeader(a){const b=a['slice'](0x0,0x20),c=new DataView(a['buffer'],a['byteOffset'],a['byteLength']),d=c['getUint32'](0x20,![]),e=c['getUint32'](0x24,![]);return{'dhPublicKey':b,'previousSendingCount':d,'messageCount':e};}function makeSkipKey(a,b){const c=new Uint8Array(a['length']+0x4);return c['set'](a,0x0),new DataView(c['buffer'])['setUint32'](a['length'],b,![]),Buffer['from'](c)['toString']('base64');}export function initRatchet(a,b,c,d,e){return{'rootKey':a,'sendingChainKey':b,'sendingMessageCount':0x0,'receivingChainKey':c,'receivingMessageCount':0x0,'ourDHKeyPair':d,'peerDHPublicKey':e,'previousSendingCount':0x0,'skipMessageKeys':new Map()};}export function ratchetEncrypt(a,b){const c=cloneRatchetState(a),{messageKey:d,nextChainKey:e}=hkdfMessageKey(c['sendingChainKey']);c['sendingChainKey']=e;const f={'dhPublicKey':c['ourDHKeyPair']['publicKey'],'previousSendingCount':c['previousSendingCount'],'messageCount':c['sendingMessageCount']},g=encodeHeader(f),{ciphertext:h,iv:i,tag:j}=aesGcmEncrypt(d,b,g),k=concatBytes(g,i,j,h);return c['sendingMessageCount']+=0x1,{'message':{'type':MessageType['NORMAL_MESSAGE'],'body':k},'newState':c};}export function ratchetDecrypt(a,b){const c=0x20+0x4+0x4;if(b['length']<c+0xc+0x10)throw new PQError(PQErrorCode['DECRYPTION_FAILED'],'Message\x20body\x20too\x20short');const d=b['slice'](0x0,c),e=decodeHeader(d),f=b['slice'](c,c+0xc),g=b['slice'](c+0xc,c+0xc+0x10),h=b['slice'](c+0xc+0x10),i=cloneRatchetState(a);let j;if(i['peerDHPublicKey']&&Buffer['from'](e['dhPublicKey'])['equals'](Buffer['from'](i['peerDHPublicKey']))){if(e['messageCount']<i['receivingMessageCount']){j=trySkippedMessageKeys(i,e);if(!j)throw new PQError(PQErrorCode['DECRYPTION_FAILED'],'Message\x20key\x20not\x20found\x20for\x20old\x20message');}else{while(i['receivingMessageCount']<e['messageCount']){const {messageKey:n,nextChainKey:o}=hkdfMessageKey(i['receivingChainKey']),p=makeSkipKey(e['dhPublicKey'],i['receivingMessageCount']);i['skipMessageKeys']['set'](p,n),i['receivingChainKey']=o,i['receivingMessageCount']+=0x1;}const {messageKey:l,nextChainKey:m}=hkdfMessageKey(i['receivingChainKey']);j=l,i['receivingChainKey']=m,i['receivingMessageCount']+=0x1;}}else{j=trySkippedMessageKeys(i,e);if(!j){const q=dhRatchetStep(i,e);Object['assign'](i,q['newState']),j=q['messageKey'];}}const k=aesGcmDecrypt(j,h,f,g,d);return{'plaintext':k,'newState':i};}function dhRatchetStep(a,b){a['previousSendingCount']=a['sendingMessageCount'];const c=x25519DH(a['ourDHKeyPair']['privateKey'],b['dhPublicKey']),{rootKey:d,chainKey:e}=hkdfChainKey(a['rootKey'],c),f=generateX25519KeyPair(),g=x25519DH(f['privateKey'],b['dhPublicKey']),{rootKey:h,chainKey:i}=hkdfChainKey(d,g),{messageKey:j}=hkdfMessageKey(e);return{'messageKey':j,'newState':{'rootKey':h,'sendingChainKey':e,'sendingMessageCount':0x0,'receivingChainKey':i,'receivingMessageCount':0x1,'ourDHKeyPair':f,'peerDHPublicKey':b['dhPublicKey'],'previousSendingCount':b['previousSendingCount']}};}function trySkippedMessageKeys(a,b){const c=makeSkipKey(b['dhPublicKey'],b['messageCount']),d=a['skipMessageKeys']['get'](c);if(d)return a['skipMessageKeys']['delete'](c),d;return null;}function cloneRatchetState(a){return{'rootKey':new Uint8Array(a['rootKey']),'sendingChainKey':new Uint8Array(a['sendingChainKey']),'sendingMessageCount':a['sendingMessageCount'],'receivingChainKey':new Uint8Array(a['receivingChainKey']),'receivingMessageCount':a['receivingMessageCount'],'ourDHKeyPair':{'publicKey':new Uint8Array(a['ourDHKeyPair']['publicKey']),'privateKey':new Uint8Array(a['ourDHKeyPair']['privateKey'])},'peerDHPublicKey':a['peerDHPublicKey']?new Uint8Array(a['peerDHPublicKey']):null,'previousSendingCount':a['previousSendingCount'],'skipMessageKeys':new Map(a['skipMessageKeys'])};}export function serializeRatchetState(a){const b={};for(const [c,d]of a['skipMessageKeys']){b[c]=Buffer['from'](d)['toString']('base64');}return{'rootKey':Buffer['from'](a['rootKey'])['toString']('base64'),'sendingChainKey':Buffer['from'](a['sendingChainKey'])['toString']('base64'),'sendingMessageCount':a['sendingMessageCount'],'receivingChainKey':Buffer['from'](a['receivingChainKey'])['toString']('base64'),'receivingMessageCount':a['receivingMessageCount'],'ourDHPrivateKey':Buffer['from'](a['ourDHKeyPair']['privateKey'])['toString']('base64'),'ourDHPublicKey':Buffer['from'](a['ourDHKeyPair']['publicKey'])['toString']('base64'),'peerDHPublicKey':a['peerDHPublicKey']?Buffer['from'](a['peerDHPublicKey'])['toString']('base64'):null,'previousSendingCount':a['previousSendingCount'],'skipMessageKeys':b};}export function deserializeRatchetState(a){const b=new Map();for(const [c,d]of Object['entries'](a['skipMessageKeys'])){b['set'](c,new Uint8Array(Buffer['from'](d,'base64')));}return{'rootKey':new Uint8Array(Buffer['from'](a['rootKey'],'base64')),'sendingChainKey':new Uint8Array(Buffer['from'](a['sendingChainKey'],'base64')),'sendingMessageCount':a['sendingMessageCount'],'receivingChainKey':new Uint8Array(Buffer['from'](a['receivingChainKey'],'base64')),'receivingMessageCount':a['receivingMessageCount'],'ourDHKeyPair':{'publicKey':new Uint8Array(Buffer['from'](a['ourDHPublicKey'],'base64')),'privateKey':new Uint8Array(Buffer['from'](a['ourDHPrivateKey'],'base64'))},'peerDHPublicKey':a['peerDHPublicKey']?new Uint8Array(Buffer['from'](a['peerDHPublicKey'],'base64')):null,'previousSendingCount':a['previousSendingCount'],'skipMessageKeys':b};}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { X25519KeyPair, Ed25519KeyPair, MLKEMKeyPair, PQPreKeyBundle, PQIdentity, EncryptedMessage, SerializedPQSessionState } from "./types.js";
|
|
2
|
+
export interface PQSessionStore {
|
|
3
|
+
getIdentityKeyPair(): Promise<X25519KeyPair | null>;
|
|
4
|
+
setIdentityKeyPair(keyPair: X25519KeyPair): Promise<void>;
|
|
5
|
+
getEd25519KeyPair(): Promise<Ed25519KeyPair | null>;
|
|
6
|
+
setEd25519KeyPair(keyPair: Ed25519KeyPair): Promise<void>;
|
|
7
|
+
getRegistrationId(): Promise<number | null>;
|
|
8
|
+
setRegistrationId(id: number): Promise<void>;
|
|
9
|
+
getSignedPreKey(keyId: number): Promise<X25519KeyPair | null>;
|
|
10
|
+
setSignedPreKey(keyId: number, keyPair: X25519KeyPair): Promise<void>;
|
|
11
|
+
getSignedPreKeySignature(keyId: number): Promise<Uint8Array | null>;
|
|
12
|
+
setSignedPreKeySignature(keyId: number, sig: Uint8Array): Promise<void>;
|
|
13
|
+
getOneTimePreKey(keyId: number): Promise<X25519KeyPair | null>;
|
|
14
|
+
setOneTimePreKey(keyId: number, keyPair: X25519KeyPair): Promise<void>;
|
|
15
|
+
removeOneTimePreKey(keyId: number): Promise<void>;
|
|
16
|
+
getPQPreKey(keyId: number): Promise<MLKEMKeyPair | null>;
|
|
17
|
+
setPQPreKey(keyId: number, keyPair: MLKEMKeyPair): Promise<void>;
|
|
18
|
+
getPQPreKeySignature(keyId: number): Promise<Uint8Array | null>;
|
|
19
|
+
setPQPreKeySignature(keyId: number, sig: Uint8Array): Promise<void>;
|
|
20
|
+
getSession(peerAddress: string, peerDeviceId: number): Promise<SerializedPQSessionState | null>;
|
|
21
|
+
setSession(peerAddress: string, peerDeviceId: number, state: SerializedPQSessionState): Promise<void>;
|
|
22
|
+
removeSession(peerAddress: string, peerDeviceId: number): Promise<void>;
|
|
23
|
+
getPeerIdentity(peerAddress: string): Promise<Uint8Array | null>;
|
|
24
|
+
setPeerIdentity(peerAddress: string, identityKey: Uint8Array): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare class PQSessionCipher {
|
|
27
|
+
private store;
|
|
28
|
+
private identity;
|
|
29
|
+
constructor(store: PQSessionStore);
|
|
30
|
+
ensureIdentity(): Promise<PQIdentity>;
|
|
31
|
+
generatePreKeyBundle(signedPreKeyId: number, pqPreKeyId: number, oneTimePreKeyCount: number): Promise<import("./pqxdh.js").GeneratedPreKeyBundle>;
|
|
32
|
+
processPreKeyBundle(peerAddress: string, peerDeviceId: number, bundle: PQPreKeyBundle): Promise<void>;
|
|
33
|
+
processInitialMessage(peerAddress: string, peerDeviceId: number, ephemeralPublicKey: Uint8Array, mlkemCiphertext: Uint8Array, peerIdentityKey: Uint8Array, signedPreKeyId: number, oneTimePreKeyId: number | null, pqPreKeyId: number): Promise<void>;
|
|
34
|
+
encryptMessage(peerAddress: string, plaintext: Uint8Array, peerDeviceId?: number): Promise<EncryptedMessage>;
|
|
35
|
+
decryptMessage(peerAddress: string, messageBody: Uint8Array, peerDeviceId?: number): Promise<Uint8Array>;
|
|
36
|
+
hasSession(peerAddress: string, peerDeviceId?: number): Promise<boolean>;
|
|
37
|
+
private loadSession;
|
|
38
|
+
private saveSession;
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{generateX25519KeyPair,generateEd25519KeyPair,concatBytes,deriveRegistrationId,bytesToBase64,base64ToBytes}from'./crypto.js';import{pqxdhInitiate,pqxdhReceive,generatePreKeyBundle,verifyPreKeyBundle}from'./pqxdh.js';import{initRatchet,ratchetEncrypt,ratchetDecrypt,serializeRatchetState,deserializeRatchetState}from'./ratchet.js';import{initSPQR,spqrGetNextChunk,spqrReceiveChunk,spqrOnPeerPQPKReceived,serializeSPQRState,deserializeSPQRState}from'./spqr.js';import{packChunk,SHARD_SIZE}from'./erasure.js';import{MessageType,DEFAULT_DEVICE_ID,CHAIN_KEY_SIZE,PQError,PQErrorCode}from'./types.js';export class PQSessionCipher{['store'];['identity']=null;constructor(a){this['store']=a;}async['ensureIdentity'](){if(this['identity'])return this['identity'];let a=await this['store']['getIdentityKeyPair'](),b=await this['store']['getEd25519KeyPair'](),c=await this['store']['getRegistrationId']();return(!a||!b||!c)&&(a=generateX25519KeyPair(),b=generateEd25519KeyPair(),c=deriveRegistrationId(a['publicKey']),await this['store']['setIdentityKeyPair'](a),await this['store']['setEd25519KeyPair'](b),await this['store']['setRegistrationId'](c)),this['identity']={'x25519KeyPair':a,'ed25519KeyPair':b,'registrationId':c},this['identity'];}async['generatePreKeyBundle'](a,b,c){const d=await this['ensureIdentity'](),e=generatePreKeyBundle(d['x25519KeyPair'],d['ed25519KeyPair'],a,b,c);await this['store']['setSignedPreKey'](a,e['signedPreKey']['keyPair']),await this['store']['setSignedPreKeySignature'](a,e['signedPreKey']['signature']);for(const f of e['oneTimePreKeys']){await this['store']['setOneTimePreKey'](f['keyId'],f['keyPair']);}return await this['store']['setPQPreKey'](b,e['pqPreKey']['keyPair']),await this['store']['setPQPreKeySignature'](b,e['pqPreKey']['signature']),e;}async['processPreKeyBundle'](a,b,c){const d=await this['ensureIdentity']();if(!verifyPreKeyBundle(c))throw new PQError(PQErrorCode['HANDSHAKE_FAILED'],'PreKey\x20bundle\x20signature\x20verification\x20failed');const e=pqxdhInitiate(d['x25519KeyPair'],d['ed25519KeyPair'],c),f=initRatchet(e['result']['rootKey'],e['result']['chainKey'],new Uint8Array(CHAIN_KEY_SIZE),e['result']['ourDHKeyPair'],e['result']['peerDHPublicKey']),g=initSPQR(),h={'pqxdhComplete':!![],'ratchetState':f,'spqrState':g,'peerIdentityKey':c['identityKey'],'peerRegistrationId':c['registrationId'],'peerDeviceId':b,'pendingInitialMessage':{'ephemeralPublicKey':e['initialMessage']['ephemeralPublicKey'],'mlkemCiphertext':e['initialMessage']['mlkemCiphertext'],'identityKey':e['initialMessage']['identityKey'],'signedPreKeyId':c['signedPreKey']['keyId'],'oneTimePreKeyId':c['oneTimePreKey']?.['keyId']??null,'pqPreKeyId':c['pqPreKey']['keyId']}};await this['store']['setPeerIdentity'](a,c['identityKey']),await this['saveSession'](a,b,h);}async['processInitialMessage'](a,b,c,d,e,f,g,h){const i=await this['ensureIdentity'](),j=await this['store']['getSignedPreKey'](f);if(!j)throw new PQError(PQErrorCode['HANDSHAKE_FAILED'],'Signed\x20pre-key\x20not\x20found');let k=null;g!==null&&(k=await this['store']['getOneTimePreKey'](g),k&&await this['store']['removeOneTimePreKey'](g));const l=await this['store']['getPQPreKey'](h);if(!l)throw new PQError(PQErrorCode['HANDSHAKE_FAILED'],'PQ\x20pre-key\x20not\x20found');const m=pqxdhReceive(i['x25519KeyPair'],j,k,l,c,e,d),n=initRatchet(m['rootKey'],new Uint8Array(CHAIN_KEY_SIZE),m['chainKey'],m['ourDHKeyPair'],m['peerDHPublicKey']),o=initSPQR(),p={'pqxdhComplete':!![],'ratchetState':n,'spqrState':o,'peerIdentityKey':e,'peerRegistrationId':0x0,'peerDeviceId':b};await this['store']['setPeerIdentity'](a,e),await this['saveSession'](a,b,p);}async['encryptMessage'](a,b,c=DEFAULT_DEVICE_ID){const d=await this['loadSession'](a,c);if(!d)throw new PQError(PQErrorCode['SESSION_NOT_FOUND'],'No\x20session\x20established');const {message:e,newState:f}=ratchetEncrypt(d['ratchetState'],b),g=f['sendingMessageCount']+f['previousSendingCount'],{chunk:h,newState:i}=spqrGetNextChunk(d['spqrState'],g);let j;if(h){const l=packChunk(h);j=concatBytes(new Uint8Array([0x1]),e['body'],l);}else j=concatBytes(new Uint8Array([0x0]),e['body']);const k=d['pendingInitialMessage']!==undefined;if(d['pendingInitialMessage']){const m=d['pendingInitialMessage'],n=m['oneTimePreKeyId']??0xffffffff,o=new Uint8Array(0x20+0x440+0x20+0x4+0x4+0x4),p=new DataView(o['buffer']);o['set'](m['ephemeralPublicKey'],0x0),o['set'](m['mlkemCiphertext'],0x20),o['set'](m['identityKey'],0x20+0x440),p['setUint32'](0x20+0x440+0x20,m['signedPreKeyId'],![]),p['setUint32'](0x20+0x440+0x20+0x4,n,![]),p['setUint32'](0x20+0x440+0x20+0x8,m['pqPreKeyId'],![]),j=concatBytes(o,j),d['pendingInitialMessage']=undefined;}return d['ratchetState']=f,d['spqrState']=i,await this['saveSession'](a,c,d),{'type':k?MessageType['PREKEY_MESSAGE']:MessageType['NORMAL_MESSAGE'],'body':j};}async['decryptMessage'](a,b,c=DEFAULT_DEVICE_ID){const d=0x20+0x440+0x20+0x4+0x4+0x4;let e=await this['loadSession'](a,c);if(!e&&b['length']>=d){const m=new DataView(b['buffer'],b['byteOffset'],b['byteLength']),n=b['slice'](0x0,0x20),o=b['slice'](0x20,0x20+0x440),p=b['slice'](0x20+0x440,0x20+0x440+0x20),q=m['getUint32'](0x20+0x440+0x20,![]),r=m['getUint32'](0x20+0x440+0x20+0x4,![]),s=m['getUint32'](0x20+0x440+0x20+0x8,![]),t=r===0xffffffff?null:r;await this['processInitialMessage'](a,c,n,o,p,q,t,s),b=b['slice'](d),e=await this['loadSession'](a,c);}if(!e)throw new PQError(PQErrorCode['SESSION_NOT_FOUND'],'No\x20session\x20established');if(b['length']<0x1)throw new PQError(PQErrorCode['DECRYPTION_FAILED'],'Message\x20body\x20too\x20short');const f=b[0x0]===0x1,g=b['slice'](0x1);let h=null;if(f){if(g['length']<SHARD_SIZE)throw new PQError(PQErrorCode['DECRYPTION_FAILED'],'SPQR\x20chunk\x20missing');h=g['slice'](g['length']-SHARD_SIZE);}const i=f?g['slice'](0x0,g['length']-SHARD_SIZE):g,{plaintext:j,newState:k}=ratchetDecrypt(e['ratchetState'],i);let l=e['spqrState'];if(h){const u=spqrReceiveChunk(l,h);l=u['newState'];if(u['shouldSendCT']&&u['peerPQPK']){const v=spqrOnPeerPQPKReceived(l,u['peerPQPK']);l=v['newState'];}}return e['ratchetState']=k,e['spqrState']=l,await this['saveSession'](a,c,e),j;}async['hasSession'](a,b=DEFAULT_DEVICE_ID){const c=await this['loadSession'](a,b);return c!==null&&c['pqxdhComplete'];}async['loadSession'](a,b){const c=await this['store']['getSession'](a,b);if(!c)return null;const d={'pqxdhComplete':c['pqxdhComplete'],'ratchetState':deserializeRatchetState(c['ratchetState']),'spqrState':deserializeSPQRState(c['spqrState']),'peerIdentityKey':base64ToBytes(c['peerIdentityKey']),'peerRegistrationId':c['peerRegistrationId'],'peerDeviceId':c['peerDeviceId']};return c['pendingInitialMessage']&&(d['pendingInitialMessage']={'ephemeralPublicKey':base64ToBytes(c['pendingInitialMessage']['ephemeralPublicKey']),'mlkemCiphertext':base64ToBytes(c['pendingInitialMessage']['mlkemCiphertext']),'identityKey':base64ToBytes(c['pendingInitialMessage']['identityKey']),'signedPreKeyId':c['pendingInitialMessage']['signedPreKeyId'],'oneTimePreKeyId':c['pendingInitialMessage']['oneTimePreKeyId'],'pqPreKeyId':c['pendingInitialMessage']['pqPreKeyId']}),d;}async['saveSession'](a,b,c){const d={'pqxdhComplete':c['pqxdhComplete'],'ratchetState':serializeRatchetState(c['ratchetState']),'spqrState':serializeSPQRState(c['spqrState']),'peerIdentityKey':bytesToBase64(c['peerIdentityKey']),'peerRegistrationId':c['peerRegistrationId'],'peerDeviceId':c['peerDeviceId']};c['pendingInitialMessage']&&(d['pendingInitialMessage']={'ephemeralPublicKey':bytesToBase64(c['pendingInitialMessage']['ephemeralPublicKey']),'mlkemCiphertext':bytesToBase64(c['pendingInitialMessage']['mlkemCiphertext']),'identityKey':bytesToBase64(c['pendingInitialMessage']['identityKey']),'signedPreKeyId':c['pendingInitialMessage']['signedPreKeyId'],'oneTimePreKeyId':c['pendingInitialMessage']['oneTimePreKeyId'],'pqPreKeyId':c['pendingInitialMessage']['pqPreKeyId']}),await this['store']['setSession'](a,b,d);}}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SPQRState, SPQRChunk, SerializedSPQRState } from "./types.js";
|
|
2
|
+
export declare function initSPQR(): SPQRState;
|
|
3
|
+
export declare function spqrGetNextChunk(state: SPQRState, messageCount: number): {
|
|
4
|
+
chunk: SPQRChunk | null;
|
|
5
|
+
newState: SPQRState;
|
|
6
|
+
};
|
|
7
|
+
export declare function spqrProcessReceivedChunks(state: SPQRState, chunks: SPQRChunk[]): {
|
|
8
|
+
newState: SPQRState;
|
|
9
|
+
completed: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function spqrOnPeerPQPKReceived(state: SPQRState, peerPQPK: Uint8Array): {
|
|
12
|
+
newState: SPQRState;
|
|
13
|
+
ctChunks: Uint8Array[];
|
|
14
|
+
};
|
|
15
|
+
export declare function spqrReceiveChunk(state: SPQRState, packedChunk: Uint8Array): {
|
|
16
|
+
newState: SPQRState;
|
|
17
|
+
peerPQPKReady: boolean;
|
|
18
|
+
peerPQPK: Uint8Array | null;
|
|
19
|
+
shouldSendCT: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function spqrGetCurrentKey(state: SPQRState): Uint8Array;
|
|
22
|
+
export declare function serializeSPQRState(state: SPQRState): SerializedSPQRState;
|
|
23
|
+
export declare function deserializeSPQRState(serialized: SerializedSPQRState): SPQRState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{generateMLKEMKeyPair,mlkemEncapsulate,mlkemDecapsulate,hkdfDerive,concatBytes}from'./crypto.js';import{encodeShards,decodeShards,unpackChunk,DATA_SHARDS}from'./erasure.js';import{CHAIN_KEY_SIZE,MESSAGE_KEY_SIZE,MLKEM_PUBLIC_KEY_SIZE,MLKEM_CIPHERTEXT_SIZE}from'./types.js';const SPQR_CYCLE_INTERVAL=0x32;export function initSPQR(){return{'cycleId':0x0,'sendingPQKeys':null,'sendingChunks':[],'sendingChunkIndex':0x0,'sendingPhase':'idle','receivedChunks':new Map(),'receivingPhase':'idle','expectedTotalChunks':0x0,'receivedSharedSecret':null,'currentSPQRKey':new Uint8Array(MESSAGE_KEY_SIZE),'spqrChainKey':new Uint8Array(CHAIN_KEY_SIZE),'spqrMessageCount':0x0};}export function spqrGetNextChunk(a,b){const c=cloneSPQRState(a);if(c['sendingPhase']==='idle'&&b>0x0&&b%SPQR_CYCLE_INTERVAL===0x0){c['cycleId']+=0x1,c['sendingPQKeys']=generateMLKEMKeyPair(),c['sendingPhase']='sending_pk';const f=encodeShards(c['sendingPQKeys']['publicKey']);c['sendingChunks']=f,c['sendingChunkIndex']=0x0;}if(c['sendingPhase']==='idle')return{'chunk':null,'newState':c};if(c['sendingChunkIndex']>=c['sendingChunks']['length']){if(c['sendingPhase']==='sending_pk')c['sendingPhase']='sending_ct_no_pk',c['sendingChunkIndex']=0x0,c['sendingChunks']=[];else{if(c['sendingPhase']==='sending_ct_no_pk'||c['sendingPhase']==='sending_ct_pk')return c['sendingPhase']='idle',c['sendingPQKeys']=null,c['sendingChunks']=[],c['sendingChunkIndex']=0x0,{'chunk':null,'newState':c};}}if((c['sendingPhase']==='sending_ct_no_pk'||c['sendingPhase']==='sending_ct_pk')&&c['sendingChunks']['length']===0x0)return{'chunk':null,'newState':c};const d=c['sendingChunks'][c['sendingChunkIndex']],e={'cycleId':c['cycleId'],'chunkIndex':c['sendingChunkIndex'],'totalChunks':c['sendingChunks']['length'],'data':d};return c['sendingChunkIndex']+=0x1,{'chunk':e,'newState':c};}export function spqrProcessReceivedChunks(a,b){const c=cloneSPQRState(a);for(const d of b){c['receivingPhase']==='idle'&&(c['receivingPhase']='receiving_ct',c['expectedTotalChunks']=d['totalChunks'],c['receivedChunks']=new Map());if(d['cycleId']!==c['cycleId'])continue;c['receivedChunks']['set'](d['chunkIndex'],d['data']);}if(c['receivingPhase']==='receiving_ct'&&c['receivedChunks']['size']>=DATA_SHARDS){const e=decodeShards(c['receivedChunks'],c['expectedTotalChunks']),f=e['slice'](0x0,MLKEM_CIPHERTEXT_SIZE);if(c['sendingPQKeys']){const g=mlkemDecapsulate(f,c['sendingPQKeys']['secretKey']);return c['receivedSharedSecret']=g,c['currentSPQRKey']=hkdfDerive(concatBytes(c['currentSPQRKey'],g),new TextEncoder()['encode']('WowokSPQRUpdate'),MESSAGE_KEY_SIZE),c['receivingPhase']='done',c['receivedChunks']=new Map(),{'newState':c,'completed':!![]};}}return{'newState':c,'completed':![]};}export function spqrOnPeerPQPKReceived(a,b){const c=cloneSPQRState(a),{ciphertext:d,sharedSecret:e}=mlkemEncapsulate(b);c['currentSPQRKey']=hkdfDerive(concatBytes(c['currentSPQRKey'],e),new TextEncoder()['encode']('WowokSPQRUpdate'),MESSAGE_KEY_SIZE);const f=encodeShards(d);return c['sendingChunks']=f,c['sendingChunkIndex']=0x0,c['sendingPhase']='sending_ct_pk',{'newState':c,'ctChunks':f};}export function spqrReceiveChunk(a,b){const c=cloneSPQRState(a),d=unpackChunk(b);c['receivingPhase']==='idle'&&(c['receivingPhase']='receiving_pk',c['expectedTotalChunks']=d['totalChunks'],c['receivedChunks']=new Map(),c['cycleId']=d['cycleId']);if(d['cycleId']!==c['cycleId'])return{'newState':c,'peerPQPKReady':![],'peerPQPK':null,'shouldSendCT':![]};c['receivedChunks']['set'](d['chunkIndex'],d['data']);if(c['receivingPhase']==='receiving_pk'&&c['receivedChunks']['size']>=DATA_SHARDS){const e=decodeShards(c['receivedChunks'],c['expectedTotalChunks']),f=e['slice'](0x0,MLKEM_PUBLIC_KEY_SIZE);return c['receivingPhase']='receiving_ct',c['receivedChunks']=new Map(),{'newState':c,'peerPQPKReady':!![],'peerPQPK':f,'shouldSendCT':!![]};}if(c['receivingPhase']==='receiving_ct'&&c['receivedChunks']['size']>=DATA_SHARDS){const g=decodeShards(c['receivedChunks'],c['expectedTotalChunks']),h=g['slice'](0x0,MLKEM_CIPHERTEXT_SIZE);if(c['sendingPQKeys']){const i=mlkemDecapsulate(h,c['sendingPQKeys']['secretKey']);c['currentSPQRKey']=hkdfDerive(concatBytes(c['currentSPQRKey'],i),new TextEncoder()['encode']('WowokSPQRUpdate'),MESSAGE_KEY_SIZE),c['receivingPhase']='done',c['receivedChunks']=new Map(),c['sendingPQKeys']=null;}return{'newState':c,'peerPQPKReady':![],'peerPQPK':null,'shouldSendCT':![]};}return{'newState':c,'peerPQPKReady':![],'peerPQPK':null,'shouldSendCT':![]};}export function spqrGetCurrentKey(a){return a['currentSPQRKey'];}function cloneSPQRState(a){return{'cycleId':a['cycleId'],'sendingPQKeys':a['sendingPQKeys']?{'publicKey':new Uint8Array(a['sendingPQKeys']['publicKey']),'secretKey':new Uint8Array(a['sendingPQKeys']['secretKey'])}:null,'sendingChunks':a['sendingChunks']['map'](b=>new Uint8Array(b)),'sendingChunkIndex':a['sendingChunkIndex'],'sendingPhase':a['sendingPhase'],'receivedChunks':new Map(a['receivedChunks']),'receivingPhase':a['receivingPhase'],'expectedTotalChunks':a['expectedTotalChunks'],'receivedSharedSecret':a['receivedSharedSecret']?new Uint8Array(a['receivedSharedSecret']):null,'currentSPQRKey':new Uint8Array(a['currentSPQRKey']),'spqrChainKey':new Uint8Array(a['spqrChainKey']),'spqrMessageCount':a['spqrMessageCount']};}export function serializeSPQRState(a){const b={};for(const [c,d]of a['receivedChunks']){b[c['toString']()]=Buffer['from'](d)['toString']('base64');}return{'cycleId':a['cycleId'],'sendingPQPublicKey':a['sendingPQKeys']?Buffer['from'](a['sendingPQKeys']['publicKey'])['toString']('base64'):null,'sendingPQSecretKey':a['sendingPQKeys']?Buffer['from'](a['sendingPQKeys']['secretKey'])['toString']('base64'):null,'sendingChunks':a['sendingChunks']['map'](e=>Buffer['from'](e)['toString']('base64')),'sendingChunkIndex':a['sendingChunkIndex'],'sendingPhase':a['sendingPhase'],'receivedChunks':b,'receivingPhase':a['receivingPhase'],'expectedTotalChunks':a['expectedTotalChunks'],'receivedSharedSecret':a['receivedSharedSecret']?Buffer['from'](a['receivedSharedSecret'])['toString']('base64'):null,'currentSPQRKey':Buffer['from'](a['currentSPQRKey'])['toString']('base64'),'spqrChainKey':Buffer['from'](a['spqrChainKey'])['toString']('base64'),'spqrMessageCount':a['spqrMessageCount']};}export function deserializeSPQRState(a){const b=new Map();for(const [c,d]of Object['entries'](a['receivedChunks'])){b['set'](parseInt(c),new Uint8Array(Buffer['from'](d,'base64')));}return{'cycleId':a['cycleId'],'sendingPQKeys':a['sendingPQPublicKey']&&a['sendingPQSecretKey']?{'publicKey':new Uint8Array(Buffer['from'](a['sendingPQPublicKey'],'base64')),'secretKey':new Uint8Array(Buffer['from'](a['sendingPQSecretKey'],'base64'))}:null,'sendingChunks':a['sendingChunks']['map'](e=>new Uint8Array(Buffer['from'](e,'base64'))),'sendingChunkIndex':a['sendingChunkIndex'],'sendingPhase':a['sendingPhase'],'receivedChunks':b,'receivingPhase':a['receivingPhase'],'expectedTotalChunks':a['expectedTotalChunks'],'receivedSharedSecret':a['receivedSharedSecret']?new Uint8Array(Buffer['from'](a['receivedSharedSecret'],'base64')):null,'currentSPQRKey':new Uint8Array(Buffer['from'](a['currentSPQRKey'],'base64')),'spqrChainKey':new Uint8Array(Buffer['from'](a['spqrChainKey'],'base64')),'spqrMessageCount':a['spqrMessageCount']};}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { X25519KeyPair, Ed25519KeyPair, MLKEMKeyPair, SerializedPQSessionState } from "./types.js";
|
|
3
|
+
import type { PQSessionStore } from "./session.js";
|
|
4
|
+
export declare class PQSessionStoreAdapter implements PQSessionStore {
|
|
5
|
+
private db;
|
|
6
|
+
private userAddress;
|
|
7
|
+
constructor(db: Database.Database, userAddress: string);
|
|
8
|
+
private getValue;
|
|
9
|
+
private putValue;
|
|
10
|
+
private delValue;
|
|
11
|
+
getIdentityKeyPair(): Promise<X25519KeyPair | null>;
|
|
12
|
+
setIdentityKeyPair(keyPair: X25519KeyPair): Promise<void>;
|
|
13
|
+
getEd25519KeyPair(): Promise<Ed25519KeyPair | null>;
|
|
14
|
+
setEd25519KeyPair(keyPair: Ed25519KeyPair): Promise<void>;
|
|
15
|
+
getRegistrationId(): Promise<number | null>;
|
|
16
|
+
setRegistrationId(id: number): Promise<void>;
|
|
17
|
+
getSignedPreKey(keyId: number): Promise<X25519KeyPair | null>;
|
|
18
|
+
setSignedPreKey(keyId: number, keyPair: X25519KeyPair): Promise<void>;
|
|
19
|
+
getSignedPreKeySignature(keyId: number): Promise<Uint8Array | null>;
|
|
20
|
+
setSignedPreKeySignature(keyId: number, sig: Uint8Array): Promise<void>;
|
|
21
|
+
getOneTimePreKey(keyId: number): Promise<X25519KeyPair | null>;
|
|
22
|
+
setOneTimePreKey(keyId: number, keyPair: X25519KeyPair): Promise<void>;
|
|
23
|
+
removeOneTimePreKey(keyId: number): Promise<void>;
|
|
24
|
+
getPQPreKey(keyId: number): Promise<MLKEMKeyPair | null>;
|
|
25
|
+
setPQPreKey(keyId: number, keyPair: MLKEMKeyPair): Promise<void>;
|
|
26
|
+
getPQPreKeySignature(keyId: number): Promise<Uint8Array | null>;
|
|
27
|
+
setPQPreKeySignature(keyId: number, sig: Uint8Array): Promise<void>;
|
|
28
|
+
getSession(peerAddress: string, peerDeviceId: number): Promise<SerializedPQSessionState | null>;
|
|
29
|
+
setSession(peerAddress: string, peerDeviceId: number, state: SerializedPQSessionState): Promise<void>;
|
|
30
|
+
removeSession(peerAddress: string, peerDeviceId: number): Promise<void>;
|
|
31
|
+
getPeerIdentity(peerAddress: string): Promise<Uint8Array | null>;
|
|
32
|
+
setPeerIdentity(peerAddress: string, identityKey: Uint8Array): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const PQ_STORE_PREFIX='pq';function arrayBufferToBase64(a){return Buffer['from'](a)['toString']('base64');}function base64ToUint8Array(a){return new Uint8Array(Buffer['from'](a,'base64'));}export class PQSessionStoreAdapter{['db'];['userAddress'];constructor(a,b){this['db']=a,this['userAddress']=b;}['getValue'](a){const b=this['db']['prepare']('SELECT\x20value\x20FROM\x20signal_store\x20WHERE\x20store_type\x20=\x20?\x20AND\x20key\x20=\x20?')['get'](PQ_STORE_PREFIX,a);return b?.['value'];}['putValue'](a,b){this['db']['prepare']('INSERT\x20INTO\x20signal_store\x20(store_type,\x20key,\x20value,\x20updated_at)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20VALUES\x20(?,\x20?,\x20?,\x20strftime(\x27%s\x27,\x20\x27now\x27))\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20ON\x20CONFLICT(store_type,\x20key)\x20DO\x20UPDATE\x20SET\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20value\x20=\x20excluded.value,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20updated_at\x20=\x20excluded.updated_at')['run'](PQ_STORE_PREFIX,a,b);}['delValue'](a){this['db']['prepare']('DELETE\x20FROM\x20signal_store\x20WHERE\x20store_type\x20=\x20?\x20AND\x20key\x20=\x20?')['run'](PQ_STORE_PREFIX,a);}async['getIdentityKeyPair'](){const a=this['getValue']('identity_x25519');if(!a)return null;const b=JSON['parse'](a);return{'publicKey':base64ToUint8Array(b['pubKey']),'privateKey':base64ToUint8Array(b['privKey'])};}async['setIdentityKeyPair'](a){const b={'pubKey':arrayBufferToBase64(a['publicKey']),'privKey':arrayBufferToBase64(a['privateKey'])};this['putValue']('identity_x25519',JSON['stringify'](b));}async['getEd25519KeyPair'](){const a=this['getValue']('identity_ed25519');if(!a)return null;const b=JSON['parse'](a);return{'publicKey':base64ToUint8Array(b['pubKey']),'privateKey':base64ToUint8Array(b['privKey'])};}async['setEd25519KeyPair'](a){const b={'pubKey':arrayBufferToBase64(a['publicKey']),'privKey':arrayBufferToBase64(a['privateKey'])};this['putValue']('identity_ed25519',JSON['stringify'](b));}async['getRegistrationId'](){const a=this['getValue']('registration_id');return a?parseInt(a,0xa):null;}async['setRegistrationId'](a){this['putValue']('registration_id',a['toString']());}async['getSignedPreKey'](a){const b=this['getValue']('signed_prekey:'+a);if(!b)return null;const c=JSON['parse'](b);return{'publicKey':base64ToUint8Array(c['pubKey']),'privateKey':base64ToUint8Array(c['privKey'])};}async['setSignedPreKey'](a,b){const c={'pubKey':arrayBufferToBase64(b['publicKey']),'privKey':arrayBufferToBase64(b['privateKey'])};this['putValue']('signed_prekey:'+a,JSON['stringify'](c));}async['getSignedPreKeySignature'](a){const b=this['getValue']('signed_prekey_sig:'+a);return b?base64ToUint8Array(b):null;}async['setSignedPreKeySignature'](a,b){this['putValue']('signed_prekey_sig:'+a,arrayBufferToBase64(b));}async['getOneTimePreKey'](a){const b=this['getValue']('onetime_prekey:'+a);if(!b)return null;const c=JSON['parse'](b);return{'publicKey':base64ToUint8Array(c['pubKey']),'privateKey':base64ToUint8Array(c['privKey'])};}async['setOneTimePreKey'](a,b){const c={'pubKey':arrayBufferToBase64(b['publicKey']),'privKey':arrayBufferToBase64(b['privateKey'])};this['putValue']('onetime_prekey:'+a,JSON['stringify'](c));}async['removeOneTimePreKey'](a){this['delValue']('onetime_prekey:'+a);}async['getPQPreKey'](a){const b=this['getValue']('pq_prekey:'+a);if(!b)return null;const c=JSON['parse'](b);return{'publicKey':base64ToUint8Array(c['pubKey']),'secretKey':base64ToUint8Array(c['secKey'])};}async['setPQPreKey'](a,b){const c={'pubKey':arrayBufferToBase64(b['publicKey']),'secKey':arrayBufferToBase64(b['secretKey'])};this['putValue']('pq_prekey:'+a,JSON['stringify'](c));}async['getPQPreKeySignature'](a){const b=this['getValue']('pq_prekey_sig:'+a);return b?base64ToUint8Array(b):null;}async['setPQPreKeySignature'](a,b){this['putValue']('pq_prekey_sig:'+a,arrayBufferToBase64(b));}async['getSession'](a,b){const c='session:'+a+'.'+b,d=this['getValue'](c);if(!d)return null;return JSON['parse'](d);}async['setSession'](a,b,c){const d='session:'+a+'.'+b;this['putValue'](d,JSON['stringify'](c));}async['removeSession'](a,b){const c='session:'+a+'.'+b;this['delValue'](c);}async['getPeerIdentity'](a){const b=this['getValue']('peer_identity:'+a);return b?base64ToUint8Array(b):null;}async['setPeerIdentity'](a,b){this['putValue']('peer_identity:'+a,arrayBufferToBase64(b));}}
|