@oppo-minigame/cli
Version:
Command line interface for rapid OPPO minigame development
1 lines • 7.62 kB
JavaScript
;const fs=require("fs-extra"),path=require("path"),crypto=require("crypto"),coder=require("./signature"),rs=require("jsrsasign"),{colorconsole:colorconsole}=require("./utils");function signZipBuffer(e,t,n){const s={signedBuffer:null,errMsg:""},r=Buffer.from(coder.Base64.unarmor(n)),i=new rs.X509;i.readCertPEM(n.toString());const a=rs.KEYUTIL.getPEM(i.getPublicKey()),o=e.buffer;if(o.length<=4)return s.errMsg="压缩数据无效",s;if("4034b50"!==o.readInt32LE(0).toString(16).toLowerCase())return s.errMsg="压缩数据类型错误",s;const l=parserZip(o);return l.options=e,l.tag?(Object.keys(l.sections).forEach(e=>{const n=l.sections[e];processChunk(o,n,t)}),signChunk(l,t,a,r),s.signedBuffer=generateChunkBuffer(o,l),s):(s.errMsg="数据解析失败",s)}function signZip(e,t,n,s){const r=Buffer.from(coder.Base64.unarmor(n)),i=new rs.X509;i.readCertPEM(n.toString());const a=rs.KEYUTIL.getPEM(i.getPublicKey()),o=fs.readFileSync(e.zip);if(!o||o.length<=4)return colorconsole.error("### App Loader ### Zip文件打开失败:",e.zip),!1;if("4034b50"!==o.readInt32LE(0).toString(16).toLowerCase())return colorconsole.error("### App Loader ### Zip文件格式错误:",e.zip),!1;const l=parserZip(o);return l.options=e,l.tag&&(Object.keys(l.sections).forEach(e=>{const n=l.sections[e];processChunk(o,n,t)}),signChunk(l,t,a,r),s||(s=makeSignFile(e.zip)),saveChunk(o,l,s)),!0}function parserZip(e){const t={tag:!1,length:e.length,sections:{header:null,central:null,footer:null}};return t.sections.footer=readEOCD(e),t.sections.footer.tag&&(t.sections.central=readCD(e,t.sections.footer.previous,t.sections.footer.startIndex-t.sections.footer.previous),t.sections.central.tag&&(t.sections.header=readFH(e,t.sections.central.previous,t.sections.central.startIndex-t.sections.central.previous),t.sections.header.tag&&(t.tag=!0))),t}function readEOCD(e){const t={tag:!1};if(e&&e.length>=22){let n,s=e.length-22;for(;s>=0;){if(n=e.readInt32LE(s),"6054b50"===n.toString(16).toLowerCase()){t.tag=!0,t.startIndex=s,t.len=e.length-s,t.previous=e.readInt32LE(s+16);break}s-=1}}return t}function readCD(e,t,n){const s={tag:!1};if(e&&e.length>=t){"2014b50"===e.readInt32LE(t).toString(16).toLowerCase()&&(s.tag=!0,s.startIndex=t,s.len=n,s.previous=e.readInt32LE(t+42))}return s}function readFH(e,t,n){const s={tag:!1};if(e&&e.length>=t){"4034b50"===e.readInt32LE(t).toString(16).toLowerCase()&&(s.tag=!0,s.startIndex=t,s.len=n,s.previous=-1)}return s}function processChunk(e,t,n){const s=t.startIndex,r=t.startIndex+t.len,i=e.slice(s,r),a=Buffer.alloc(5+t.len);a[0]=165,a.writeInt32LE(i.length,1),i.copy(a,5);const o=crypto.createHash("SHA256");o.update(a),t.sign=o.digest()}function hashFile(e,t){return hashFileBuffer(t.readFileSync(e))}function hashFileBuffer(e){const t=crypto.createHash("SHA256");return t.update(e),t.digest()}function signChunk(e,t,n,s){const r=e.sections,i=r.header.sign.length+r.central.sign.length+r.footer.sign.length+5,a=Buffer.alloc(i);let o=0;function l(e){e.copy(a,o),o+=e.length}a.writeInt8(90,0),a.writeInt32LE(3,1),o+=5,l(r.header.sign),l(r.central.sign),l(r.footer.sign);const c=crypto.createHash("SHA256");c.update(a);const g=c.digest(),u=makeSignChunk(e.options,g,t,n,s);e.signchunk=saveSignChunk(u)}function makeSignChunk(e,t,n,s,r){const i=Buffer.alloc(t.length+12);i.writeInt32LE(t.length+8,0),i.writeInt32LE(259,4),i.writeInt32LE(t.length,8),t.copy(i,12);const a={len:i.length,buffer:i},o=Buffer.alloc(r.length+4);o.writeInt32LE(r.length,0),r.copy(o,4);const l={len:o.length,buffer:o},c={len:12,digests:{size:0,data:[]},certs:{size:0,data:[]},additional:0};c.digests.data.push(a),c.digests.size+=a.len,c.len+=a.len,c.certs.data.push(l),c.certs.size+=l.len,c.len+=l.len;const g=Buffer.from(coder.Base64.unarmor(s)),u={len:16+g.length,size:12+g.length,signdata:{size:0,buffer:null},signatures:{size:0,data:[]},pubkey:{size:g.length,buffer:g}};u.signdata.buffer=makeSignDataBuffer(c),u.signdata.size=c.len,u.size+=c.len,u.len+=c.len;const f=crypto.createSign("RSA-SHA256");f.update(u.signdata.buffer);const h=f.sign(n),d={len:h.length+12,size:h.length+8,id:259,buffer:h};u.signatures.data.push(d),u.signatures.size+=d.len,u.size+=d.len,u.len+=d.len;const p={len:4,size:0,data:[]};p.data.push(u),p.size+=u.len,p.len+=u.len;const E={len:p.len+12,size:p.len+4,id:16777473,value:p},I={len:32,size:24,data:[]};if(I.data.push(E),I.size+=E.len,I.len+=E.len,e.files){const t=signFiles(e.files,n);if(t){const e={len:4,size:0,data:[]};e.data.push(t),e.size+=t.length,e.len+=t.length;const n={len:e.len+12,size:e.len+4,id:16777729,value:e};I.data.push(n),I.size+=n.len,I.len+=n.len}}return I}function makeSignDataBuffer(e){const t=Buffer.alloc(e.len);let n=0;return t.writeInt32LE(e.digests.size,n),n+=4,e.digests.data.forEach(e=>{e.buffer.copy(t,n),n+=e.len}),t.writeInt32LE(e.certs.size,n),n+=4,e.certs.data.forEach(e=>{e.buffer.copy(t,n),n+=e.len}),t.writeInt32LE(e.additional,n),t}const SigMagic="RPK Sig Block 42";function saveSignChunk(e){const t=Buffer.alloc(e.len);let n=0;t.writeInt32LE(e.size,n),n+=4,t.writeInt32LE(0,n),n+=4,e.data.forEach(e=>{t.writeInt32LE(e.size,n),n+=4,t.writeInt32LE(0,n),n+=4,t.writeInt32LE(e.id,n),n+=4,t.writeInt32LE(e.value.size,n),n+=4,16777473===e.id?e.value.data.forEach(e=>{t.writeInt32LE(e.size,n),n+=4,t.writeInt32LE(e.signdata.size,n),n+=4,e.signdata.buffer.copy(t,n),n+=e.signdata.buffer.length,t.writeInt32LE(e.signatures.size,n),n+=4,e.signatures.data.forEach(e=>{t.writeInt32LE(e.size,n),n+=4,t.writeInt32LE(e.id,n),n+=4,t.writeInt32LE(e.buffer.length,n),n+=4,e.buffer.copy(t,n),n+=e.buffer.length}),t.writeInt32LE(e.pubkey.size,n),n+=4,e.pubkey.buffer.copy(t,n),n+=e.pubkey.buffer.length}):16777729===e.id&&e.value.data.forEach(e=>{e.copy(t,n),n+=e.length})}),t.writeInt32LE(e.size,n),n+=4,t.writeInt32LE(0,n),n+=4;return Buffer.from(SigMagic).copy(t,n),t}function makeSignFile(e){const t=path.extname(e),n=path.dirname(e),s=path.basename(e,t);return path.join(n,s+".signed"+t)}function generateChunkBuffer(e,t){const n=Buffer.alloc(e.length+t.signchunk.length);let s=0;const r=t.sections;return e.copy(n,s,r.header.startIndex,r.header.startIndex+r.header.len),s+=r.header.len,t.signchunk.copy(n,s),s+=t.signchunk.length,e.copy(n,s,r.central.startIndex,r.central.startIndex+r.central.len),s+=r.central.len,e.writeInt32LE(r.central.startIndex+t.signchunk.length,r.footer.startIndex+16),e.copy(n,s,r.footer.startIndex,r.footer.startIndex+r.footer.len),s+=r.footer.len,n}function saveChunk(e,t,n){const s=generateChunkBuffer(e,t);fs.writeFileSync(n,s)}function signFiles(e,t,n){const s={len:8,size:4,digests:[],sign:null};return e.forEach(e=>{const t=coder.CRC32.digest(e.name.toString()),n=6+e.hash.length,r=Buffer.alloc(n);let i=0;r.writeInt32LE(t,i),i+=4,r.writeInt16LE(e.hash.length,i),i+=2,e.hash.copy(r,i),i+=e.hash.length,s.digests.push(r),s.size+=n,s.len+=n}),signDigestChunk(s,t),saveDigestChunk(s,n)}function signDigestChunk(e,t){const n=Buffer.alloc(e.size);let s=0;n.writeInt32LE(259,s),s+=4,e.digests.forEach(e=>{e.copy(n,s),s+=e.length}),e.digests=n.slice();const r=crypto.createSign("RSA-SHA256");r.update(n);const i=r.sign(t);e.sign={len:12+i.length,size:8+i.length,id:259,data:i},e.len+=e.sign.len}function saveDigestChunk(e,t){const n=Buffer.alloc(e.len);let s=0;return n.writeInt32LE(e.size,s),s+=4,e.digests.copy(n,s),s+=e.digests.length,n.writeInt32LE(e.sign.size,s),s+=4,n.writeInt32LE(e.sign.id,s),s+=4,n.writeInt32LE(e.sign.data.length,s),s+=4,e.sign.data.copy(n,s),s+=e.sign.data.length,t&&fs.writeFileSync(t,n),n}module.exports={signZip:signZip,signZipBuffer:signZipBuffer,hashFile:hashFile,hashFileBuffer:hashFileBuffer};