wowok
Version:
Wowok Blockchain TypeScript API
1 lines • 13.9 kB
JavaScript
import{PQSessionCipher,PQSessionStoreAdapter}from'./pq/index.js';import{MessengerServerClient}from'./server.js';import{getDatabase}from'./storage.js';import{bytesToBase64,base64ToBytes}from'./crypto.js';import{MessageType,DEFAULT_MESSENGER_CONFIG,MessengerError,MessengerErrorCode,DEFAULT_DEVICE_ID}from'./types.js';async function getAccount(){const {Account:a}=await import('../local/account.js');return a['Instance']();}export class MessengerSession{['pqStore'];['pqCipher'];['serverClient'];['config'];['identity']=null;['userAddress'];constructor(a,b){this['config']={...DEFAULT_MESSENGER_CONFIG,...b},this['serverClient']=new MessengerServerClient(this['config']),this['userAddress']=a;const c=getDatabase(a);this['pqStore']=new PQSessionStoreAdapter(c,a),this['pqCipher']=new PQSessionCipher(this['pqStore'],async()=>{const d=await getAccount(),e=await d['deriveX25519Identity'](a);return e['falcon512KeyPair'];});}get['store'](){return this['pqStore'];}get['cipher'](){return this['pqCipher'];}async['ensureIdentity'](a){if(this['identity'])return this['identity'];const b=await this['pqStore']['getIdentityKeyPair'](),c=await this['pqStore']['getRegistrationId']();if(b&&c)return this['identity']={'x25519KeyPair':b,'registrationId':c},this['identity'];const d=await getAccount(),e=await d['deriveX25519Identity'](this['userAddress']);return await this['pqStore']['setIdentityKeyPair'](e['x25519KeyPair']),await this['pqStore']['setRegistrationId'](e['registrationId']),this['identity']={'x25519KeyPair':e['x25519KeyPair'],'registrationId':e['registrationId']},this['identity'];}['deriveRegistrationId'](a){const b=a[0x0]<<0x8|a[0x1];return b===0x0?0x1:b;}async['registerDevice'](a){const b=await this['ensureIdentity'](a),c=0x1,d=0x1;let e,f;const g=await this['pqStore']['getSignedPreKeySignature'](c),h=await this['pqStore']['getSignedPreKey'](c),i=await this['pqStore']['getPQPreKey'](d),j=await this['pqStore']['getPQPreKeySignature'](d);if(h&&g&&i&&j)e=h['publicKey'],f=g;else{const u=await this['pqCipher']['generatePreKeyBundle'](c,d,this['config']['prekey_count']);e=u['signedPreKey']['keyPair']['publicKey'],f=u['signedPreKey']['signature'];}const k=await this['generatePreKeyBatch'](this['config']['prekey_count'],a),l=await(await getAccount())['get'](a,![]);if(!l?.['pubkey'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20not\x20found\x20for\x20'+a);const m=Date['now'](),n=this['generateNonce'](),o='register:'+l['pubkey']+':'+m+':'+n,p=await(await getAccount())['signData'](a,o),q=Buffer['from'](p['signature']['slice'](0x2),'hex'),r=await this['pqStore']['getPQPreKey'](d),s=await this['pqStore']['getPQPreKeySignature'](d),t={'userAddress':a,'deviceId':DEFAULT_DEVICE_ID,'registrationId':b['registrationId'],'identityKey':bytesToBase64(b['x25519KeyPair']['publicKey']),'falcon512PublicKey':bytesToBase64(base64ToBytes(l['pubkey'])['slice'](0x1)),'signedPrekey':{'keyId':c,'publicKey':bytesToBase64(e),'signature':bytesToBase64(f)},'prekeys':k,'publicKey':l['pubkey'],'signatureScheme':'Falcon512','signature':bytesToBase64(q),'timestamp':m,'nonce':n};r&&s&&(t['pqPrekey']={'keyId':d,'publicKey':bytesToBase64(r['publicKey']),'signature':bytesToBase64(s)}),await this['serverClient']['registerDevice'](t),await this['verifyRegistrationComplete'](a);}async['verifyRegistrationComplete'](a,b=0xa,c=0x1f4){for(let d=0x0;d<b;d++){try{const e=await this['serverClient']['getPrekeyStatus'](a);return;}catch(f){if(f?.['message']?.['includes']('404')||f?.['status']===0x194){if(d<b-0x1){await new Promise(g=>setTimeout(g,c));continue;}}throw f;}}throw new MessengerError(MessengerErrorCode['REGISTRATION_FAILED'],'Registration\x20verification\x20failed\x20after\x20'+b+'\x20attempts');}async['isDeviceRegistered'](a){try{const b=await this['pqStore']['getSignedPreKeySignature'](0x1);if(!b)return![];const c=await this['serverClient']['getPrekeyStatus'](a);return!![];}catch(d){if(d?.['message']?.['includes']('404')||d?.['status']===0x194)return![];return![];}}async['generatePreKeyBatch'](a,b){if(a<=0x0)return[];const c=[],{generateX25519KeyPair:d}=await import('./pq/crypto.js'),e=Math['floor'](Math['random']()*0x7fffff00);for(let f=0x0;f<a;f++){const g=e+f,h=await this['pqStore']['getOneTimePreKey'](g);if(h){const m=bytesToBase64(h['publicKey']),n=await(await getAccount())['signData'](b,h['publicKey']),o=Buffer['from'](n['signature']['slice'](0x2),'hex');c['push']({'keyId':g,'publicKey':m,'signature':bytesToBase64(o)});continue;}const j=d(),k=await(await getAccount())['signData'](b,j['publicKey']),l=Buffer['from'](k['signature']['slice'](0x2),'hex');await this['pqStore']['setOneTimePreKey'](g,j),c['push']({'keyId':g,'publicKey':bytesToBase64(j['publicKey']),'signature':bytesToBase64(l)});}return c;}async['ensurePreKeys'](a,b=![]){const c=await this['serverClient']['getPrekeyStatus'](a);if(!b&&c['currentCount']>=c['maxAllowed'])return;const d=c['maxAllowed']-c['currentCount'];if(d<=0x0)return;await this['ensureIdentity'](a);const e=await this['generatePreKeyBatch'](d,a);if(e['length']===0x0)return;const f=await(await getAccount())['get'](a,![]);if(!f?.['pubkey'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20not\x20found\x20for\x20'+a);const g=Date['now'](),h=this['generateNonce'](),i='upload_prekeys:'+f['pubkey']+':'+g+':'+h,j=await(await getAccount())['signData'](a,i),k=Buffer['from'](j['signature']['slice'](0x2),'hex');await this['serverClient']['uploadPreKeys']({'userAddress':a,'prekeys':e,'publicKey':f['pubkey'],'signatureScheme':'Falcon512','signature':bytesToBase64(k),'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['establishSession'](a,b,c=DEFAULT_DEVICE_ID,d){const e=await this['pqCipher']['hasSession'](b,c);if(e)return;if(!d){const l=await(await getAccount())['get'](a,![]);if(!l?.['pubkey'])throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'Account\x20not\x20found\x20for\x20'+a);const m=Date['now'](),n=this['generateNonce'](),o=l['pubkey'],p='get_bundle:'+a+':'+m+':'+n,q=await(await getAccount())['signData'](a,p),r=Buffer['from'](q['signature']['slice'](0x2),'hex');d=await this['serverClient']['fetchRemoteBundle'](b,a,o,{'signatureScheme':'Falcon512','signature':bytesToBase64(new Uint8Array(r)),'timestamp':m,'nonce':n},c);}const f=d['signedPrekey'];if(!f)throw new MessengerError(MessengerErrorCode['IDENTITY_NOT_FOUND'],'No\x20signed\x20prekey\x20available\x20for\x20'+b);const g=base64ToBytes(d['identityKey']),h=d['falcon512PublicKey']?base64ToBytes(d['falcon512PublicKey']):g,i=base64ToBytes(f['publicKey']),j=base64ToBytes(f['signature']),k={'registrationId':d['registrationId'],'deviceId':c,'identityKey':g,'falcon512PublicKey':h,'signedPreKey':{'keyId':f['keyId'],'publicKey':i,'signature':j},'pqPreKey':d['pqPreKey']?{'keyId':d['pqPreKey']['keyId'],'publicKey':base64ToBytes(d['pqPreKey']['publicKey']),'signature':base64ToBytes(d['pqPreKey']['signature'])}:undefined};if(d['oneTimePrekey']){const s=base64ToBytes(d['oneTimePrekey']['publicKey']);k['oneTimePreKey']={'keyId':d['oneTimePrekey']['keyId'],'publicKey':s};}if(d['pqPreKey']){const t=base64ToBytes(d['pqPreKey']['publicKey']),u=base64ToBytes(d['pqPreKey']['signature']);k['pqPreKey']={'keyId':d['pqPreKey']['keyId'],'publicKey':t,'signature':u};}await this['pqCipher']['processPreKeyBundle'](b,c,k);}async['encryptMessage'](a,b,c,d=DEFAULT_DEVICE_ID){let e=0x0;const f=0x5;while(e<=f){try{const g=await this['pqCipher']['hasSession'](b,d);!g&&(console['log']('[Session]\x20建立新会话\x20(尝试\x20'+(e+0x1)+'/'+(f+0x1)+')'),await this['establishSession'](a,b,d));const h=new TextEncoder(),i=h['encode'](c),j=await this['pqCipher']['encryptMessage'](b,i,d);return{'type':j['type'],'body':j['body']['buffer'],'registrationId':j['registrationId']};}catch(k){console['error']('[Session]\x20加密失败\x20(尝试\x20'+(e+0x1)+'/'+(f+0x1)+'):',k);if(e>=f)throw k;console['log']('[Session]\x20尝试删除旧会话并重新建立...');try{await this['pqStore']['removeSession'](b,d),e++,await new Promise(l=>setTimeout(l,0x64));}catch(l){console['error']('[Session]\x20重新建立会话失败:',l);throw k;}}}throw new MessengerError(MessengerErrorCode['ENCRYPTION_FAILED'],'Encryption\x20failed\x20after\x20multiple\x20attempts');}async['decryptMessage'](a,b,c,d,e=DEFAULT_DEVICE_ID){const f=new TextDecoder(),g=new Uint8Array(c);let h=0x0;const i=0x5,j=await this['pqCipher']['hasSession'](b,e);if(d!==MessageType['PREKEY_MESSAGE']&&!j){console['log']('[Session]\x20收到\x20WHISPER_MESSAGE\x20但无现有会话,需要等待\x20PREKEY_MESSAGE');throw new Error('收到\x20WHISPER_MESSAGE\x20但无现有会话,需要等待\x20PREKEY_MESSAGE');}while(h<=i){try{let k;if(d===MessageType['PREKEY_MESSAGE']){console['log']('[Session]\x20收到\x20PREKEY_MESSAGE,尝试解密\x20(尝试\x20'+(h+0x1)+'/'+(i+0x1)+')');try{return k=await this['pqCipher']['decryptMessage'](b,g,e),console['log']('[Session]\x20PREKEY_MESSAGE\x20解密成功!'),f['decode'](k);}catch(l){console['log']('[Session]\x20直接解密\x20PREKEY_MESSAGE\x20失败:\x20'+(l instanceof Error?l['message']:String(l)));if(j&&h===0x0){const m=a['toLowerCase'](),n=b['toLowerCase']();if(m<n){console['log']('[Session]\x20我的地址较小\x20('+m+'\x20<\x20'+n+'),保留我的会话,稍后重试');throw new Error('PREKEY\x20竞争:我的地址较小,保留我的会话');}else return console['log']('[Session]\x20我的地址较大\x20('+m+'\x20>\x20'+n+'),删除我的会话,用对方的\x20PREKEY_MESSAGE\x20建立新会话'),await this['pqStore']['removeSession'](b,e),console['log']('[Session]\x20已删除旧会话,重新尝试解密...'),k=await this['pqCipher']['decryptMessage'](b,g,e),console['log']('[Session]\x20PREKEY_MESSAGE\x20解密成功,新会话已建立'),f['decode'](k);}else throw l;}}else k=await this['pqCipher']['decryptMessage'](b,g,e);return f['decode'](k);}catch(o){console['error']('[Session\x20Debug]\x20解密失败\x20(尝试\x20'+(h+0x1)+'/'+(i+0x1)+'):'),console['error']('\x20\x20错误消息:\x20'+(o instanceof Error?o['message']:String(o))),console['error']('\x20\x20上下文:'),console['error']('\x20\x20\x20\x20-\x20myAddress:\x20'+a),console['error']('\x20\x20\x20\x20-\x20peerAddress:\x20'+b),console['error']('\x20\x20\x20\x20-\x20msgType:\x20'+d+'\x20('+(d===MessageType['PREKEY_MESSAGE']?'PREKEY':'WHISPER')+')');if(h>=i)throw o;console['log']('[Session]\x20尝试重新建立会话...');try{j&&(console['log']('[Session]\x20删除旧会话'),await this['pqStore']['removeSession'](b,e)),d===MessageType['PREKEY_MESSAGE']?console['log']('[Session]\x20收到的是\x20PREKEY_MESSAGE,等待发送方的\x20PreKey\x20即可重建会话'):console['log']('[Session]\x20收到的是\x20WHISPER_MESSAGE,需要让对方重新发送\x20PREKEY_MESSAGE'),h++,await new Promise(p=>setTimeout(p,0x64));}catch(p){console['error']('[Session]\x20重新建立会话失败:',p);throw o;}}}throw new MessengerError(MessengerErrorCode['DECRYPTION_FAILED'],'Decryption\x20failed\x20after\x20multiple\x20attempts');}async['getIdentityKey'](){const a=await this['ensureIdentity']();return bytesToBase64(a['x25519KeyPair']['publicKey']);}async['getRegistrationId'](){const a=await this['ensureIdentity']();return a['registrationId'];}}export class SessionStateManager{['store'];constructor(a){this['store']=a;}async['snapshotSession'](a,b){const c=await this['store']['getSession'](a,b);return{'sessionData':c?JSON['stringify'](c):null,'timestamp':Date['now']()};}async['rollbackSession'](a,b,c){if(c['sessionData']){const d=JSON['parse'](c['sessionData']);await this['store']['setSession'](a,b,d),console['log']('[SessionState]\x20回滚会话:\x20'+a+'.'+b);}else await this['store']['removeSession'](a,b),console['log']('[SessionState]\x20清除会话:\x20'+a+'.'+b);}async['commitSession'](a,b){console['log']('[SessionState]\x20提交新会话');}}export class DecryptionEngine{['sessionManager'];['store'];['cipher'];constructor(a){this['store']=a,this['cipher']=new PQSessionCipher(a),this['sessionManager']=new SessionStateManager(a);}async['decryptMessage'](a,b,c,d,e=DEFAULT_DEVICE_ID){const f=await this['sessionManager']['snapshotSession'](b,e);try{return d===MessageType['PREKEY_MESSAGE']?await this['handlePreKeyMessage'](a,b,c,e,f):await this['handleWhisperMessage'](a,b,c,e,f);}catch(g){return await this['sessionManager']['rollbackSession'](b,e,f),{'success':![],'sessionUpdated':![],'sessionRolledBack':!![],'messageType':d===MessageType['PREKEY_MESSAGE']?'PREKEY':'WHISPER','error':g instanceof Error?g['message']:String(g)};}}async['handlePreKeyMessage'](a,b,c,d,e){console['log']('[Decrypt]\x20处理\x20PREKEY_MESSAGE\x20from\x20'+b);const f=new TextDecoder(),g=new Uint8Array(c),h=await this['cipher']['decryptMessage'](b,g,d),i=f['decode'](h);console['log']('[Decrypt]\x20PREKEY_MESSAGE\x20解密成功'),await this['sessionManager']['commitSession'](b,d);const j=a['toLowerCase'](),k=b['toLowerCase']();return j<k?console['log']('[Decrypt]\x20小地址('+j+')保存接收会话用于解密,发送会话保持不变'):console['log']('[Decrypt]\x20大地址('+j+')接受新会话'),{'success':!![],'plaintext':i,'sessionUpdated':!![],'sessionRolledBack':![],'messageType':'PREKEY'};}async['handleWhisperMessage'](a,b,c,d,e){console['log']('[Decrypt]\x20处理\x20WHISPER_MESSAGE\x20from\x20'+b);const f=await this['cipher']['hasSession'](b,d);if(!f)throw new Error('No\x20session\x20for\x20WHISPER_MESSAGE');const g=new TextDecoder(),h=new Uint8Array(c),i=await this['cipher']['decryptMessage'](b,h,d),j=g['decode'](i);return console['log']('[Decrypt]\x20WHISPER_MESSAGE\x20解密成功'),{'success':!![],'plaintext':j,'sessionUpdated':!![],'sessionRolledBack':![],'messageType':'WHISPER'};}}