Skip to content

Instantly share code, notes, and snippets.

@A-d-i-t-h-y-a-n
Created April 12, 2025 13:36
Show Gist options
  • Save A-d-i-t-h-y-a-n/acc6fc609c1a3f91e41cd8dee6b642fb to your computer and use it in GitHub Desktop.
Save A-d-i-t-h-y-a-n/acc6fc609c1a3f91e41cd8dee6b642fb to your computer and use it in GitHub Desktop.
encryptedStream for baileys with streamingSidecar
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
const { stream, type } = await (0, exports.getStream)(media, opts);
logger?.debug('fetched media stream');
const mediaKey = Crypto.randomBytes(32);
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
const encWriteStream = new stream_1.Readable({ read: () => {} });
let bodyPath;
let writeStream;
let didSaveToTmpPath = false;
if (type === 'file') {
bodyPath = media.url.toString();
} else if (saveOriginalFileIfRequired) {
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
writeStream = (0, fs_1.createWriteStream)(bodyPath);
didSaveToTmpPath = true;
}
let fileLength = 0;
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
let hmac = Crypto.createHmac('sha256', macKey).update(iv);
let sha256Plain = Crypto.createHash('sha256');
let sha256Enc = Crypto.createHash('sha256');
// --- Streaming Sidecar ---
const chunkSize = 64 * 1024;
const hmacLength = 10;
const sidecarChunks = [];
let bufferedData = Buffer.alloc(0);
let previousChunkTail = iv;
try {
for await (const data of stream) {
fileLength += data.length;
if (type === 'remote' && opts?.maxContentLength && fileLength + data.length > opts.maxContentLength) {
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
data: { media, type }
});
}
sha256Plain = sha256Plain.update(data);
if (writeStream && !writeStream.write(data)) {
await (0, events_1.once)(writeStream, 'drain');
}
// --- Encrypt and collect encrypted data ---
const encrypted = aes.update(data);
onChunk(encrypted);
// --- Streaming Sidecar Processing ---
bufferedData = Buffer.concat([bufferedData, encrypted]);
while (bufferedData.length >= chunkSize) {
const chunk = Buffer.concat([
previousChunkTail,
bufferedData.slice(0, chunkSize)
]);
const chunkHmac = Crypto.createHmac('sha256', macKey).update(chunk).digest().slice(0, hmacLength);
sidecarChunks.push(chunkHmac);
previousChunkTail = bufferedData.slice(chunkSize - 16, chunkSize);
bufferedData = bufferedData.slice(chunkSize);
}
}
// Handle last chunk if any data remains
if (bufferedData.length > 0) {
const chunk = Buffer.concat([previousChunkTail, bufferedData]);
const chunkHmac = Crypto.createHmac('sha256', macKey).update(chunk).digest().slice(0, hmacLength);
sidecarChunks.push(chunkHmac);
}
onChunk(aes.final());
const mac = hmac.digest().slice(0, 10);
sha256Enc = sha256Enc.update(mac);
const fileSha256 = sha256Plain.digest();
const fileEncSha256 = sha256Enc.digest();
encWriteStream.push(mac);
encWriteStream.push(null);
writeStream?.end();
stream.destroy();
logger?.debug('encrypted data successfully');
console.log(Buffer.concat(sidecarChunks))
return {
mediaKey,
encWriteStream,
bodyPath,
mac,
fileEncSha256,
fileSha256,
fileLength,
didSaveToTmpPath,
streamingSidecar: Buffer.concat(sidecarChunks)
};
} catch (error) {
encWriteStream.destroy();
writeStream?.destroy();
aes.destroy();
hmac.destroy();
sha256Plain.destroy();
sha256Enc.destroy();
stream.destroy();
if (didSaveToTmpPath) {
try {
await fs_1.promises.unlink(bodyPath);
} catch (err) {
logger?.error({ err }, 'failed to save to tmp path');
}
}
throw error;
}
function onChunk(buff) {
sha256Enc = sha256Enc.update(buff);
hmac = hmac.update(buff);
encWriteStream.push(buff);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment