🚀 Use streams instead of arraybuffers
This commit is contained in:
parent
27afb62d49
commit
d79e7d289c
3 changed files with 76 additions and 14 deletions
25
src/ssn/decryption/decryptChunk.ts
Normal file
25
src/ssn/decryption/decryptChunk.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import updateKeys from './lib/updateKeys';
|
||||
|
||||
export default function decryptChunk(encryptedChunk: Buffer, [key0, key1, key2]: [number, number, number]): Buffer {
|
||||
const dv = new DataView(encryptedChunk.buffer);
|
||||
|
||||
const decryptedChunk = new Buffer(encryptedChunk.length);
|
||||
const dvOut = new DataView(decryptedChunk.buffer);
|
||||
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
//read and decrypt byte
|
||||
let curChar = dv.getUint8(i);
|
||||
const keyPart = (key2 | 2) & 0xFFFF;
|
||||
const decryptedByte = (keyPart * (keyPart ^ 1)) >>> 8;
|
||||
curChar ^= decryptedByte & 0xFF;
|
||||
//Skip the first 12 bytes (random encryption header)
|
||||
if (i >= 12) {
|
||||
dvOut.setUint8(i - 12, curChar);
|
||||
}
|
||||
|
||||
//update keys
|
||||
[key0, key1, key2] = updateKeys([key0, key1, key2], curChar);
|
||||
}
|
||||
|
||||
return decryptedChunk;
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
import * as stream from 'stream';
|
||||
import * as zlib from 'zlib';
|
||||
import { ISsnFileEntry } from '../interfaces/ISsnFileEntry';
|
||||
import decryptStream from './streams/decryptStream';
|
||||
import streamSetMaxLength from './streams/streamSetMaxLength';
|
||||
|
||||
/** Extracts the file with the given metadata from the stream.
|
||||
|
@ -25,22 +26,28 @@ export default function extractFileStream(file: ISsnFileEntry, inputStream: stre
|
|||
//skip local file name and extra field
|
||||
inputStream.read(localFilenameSize + localExtraSize);
|
||||
|
||||
//TODO: pipe into decryption if file is encrypted
|
||||
const decryptTransform = new stream.Transform({
|
||||
read() {
|
||||
//...
|
||||
},
|
||||
});
|
||||
inputStream.pipe(decryptTransform);
|
||||
//-------------------------------------------------
|
||||
|
||||
//TODO: pipe into decompression if file is compressed
|
||||
const decompressTransform = zlib.createInflateRaw();
|
||||
decryptTransform.pipe(decompressTransform);
|
||||
let curStream = inputStream;
|
||||
|
||||
//TODO: output file
|
||||
return streamSetMaxLength(decompressTransform, file.size);
|
||||
//set max length
|
||||
const maxLength = streamSetMaxLength(curStream, file.compressedSize);
|
||||
curStream = maxLength;
|
||||
|
||||
/*const out = new stream.Readable();
|
||||
|
||||
return out;*/
|
||||
//pipe into decryption if file is encrypted
|
||||
if (file.decryptionKeys !== undefined) {
|
||||
const decryptTransform = decryptStream(curStream, file.decryptionKeys);
|
||||
curStream = decryptTransform;
|
||||
}
|
||||
|
||||
//pipe into decompression
|
||||
const decompressTransform = zlib.createInflateRaw();
|
||||
curStream.pipe(decompressTransform);
|
||||
curStream = decompressTransform;
|
||||
|
||||
//set max length
|
||||
const maxLength2 = streamSetMaxLength(curStream, file.size);
|
||||
curStream = maxLength2;
|
||||
|
||||
return curStream;
|
||||
}
|
||||
|
|
30
src/ssn/streams/decryptStream.ts
Normal file
30
src/ssn/streams/decryptStream.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import * as stream from 'stream';
|
||||
import decryptChunk from '../decryption/decryptChunk';
|
||||
|
||||
export default function decryptStream(inputStream: stream.Readable, decryptionKeys: [number, number, number]): stream.Readable {
|
||||
let skippedRandomHeader = false;
|
||||
|
||||
const outStream = new stream.Readable({
|
||||
encoding: 'binary',
|
||||
read(size) {
|
||||
//There are 12 random bytes at the beginning, we need to use them to initialize the decryption keys, but we can ignore the decrypted bytes.
|
||||
if (!skippedRandomHeader) {
|
||||
const encryptedHeader = inputStream.read(12);
|
||||
decryptChunk(encryptedHeader, decryptionKeys);
|
||||
skippedRandomHeader = true;
|
||||
}
|
||||
|
||||
//Decrypt chunk
|
||||
const encryptedChunk = inputStream.read(size);
|
||||
const decryptedChunk = decryptChunk(encryptedChunk, decryptionKeys);
|
||||
|
||||
this.push(decryptedChunk);
|
||||
},
|
||||
});
|
||||
|
||||
inputStream.on('end', () => {
|
||||
outStream.emit('end');
|
||||
});
|
||||
|
||||
return outStream;
|
||||
}
|
Loading…
Reference in a new issue