🚀 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 stream from 'stream';
|
||||||
import * as zlib from 'zlib';
|
import * as zlib from 'zlib';
|
||||||
import { ISsnFileEntry } from '../interfaces/ISsnFileEntry';
|
import { ISsnFileEntry } from '../interfaces/ISsnFileEntry';
|
||||||
|
import decryptStream from './streams/decryptStream';
|
||||||
import streamSetMaxLength from './streams/streamSetMaxLength';
|
import streamSetMaxLength from './streams/streamSetMaxLength';
|
||||||
|
|
||||||
/** Extracts the file with the given metadata from the stream.
|
/** 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
|
//skip local file name and extra field
|
||||||
inputStream.read(localFilenameSize + localExtraSize);
|
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
|
let curStream = inputStream;
|
||||||
const decompressTransform = zlib.createInflateRaw();
|
|
||||||
decryptTransform.pipe(decompressTransform);
|
|
||||||
|
|
||||||
//TODO: output file
|
//set max length
|
||||||
return streamSetMaxLength(decompressTransform, file.size);
|
const maxLength = streamSetMaxLength(curStream, file.compressedSize);
|
||||||
|
curStream = maxLength;
|
||||||
|
|
||||||
/*const out = new stream.Readable();
|
//pipe into decryption if file is encrypted
|
||||||
|
if (file.decryptionKeys !== undefined) {
|
||||||
return out;*/
|
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