import { PNG } from 'pngjs' import fs from 'fs' import { createHash, createPrivateKey, createSign } from 'crypto' import { server } from './index.js' export const ImageSecurity = { createImageHash: async (stream, isCape) => { const png = await new Promise((resolve, reject) => { stream.pipe(new PNG()).on('metadata', function(metadata) { if(metadata.width * metadata.height > 40960) { reject('Image size exceeds 40960 pixels') } }).on('parsed', function(_) { resolve(this) }) }) if (isCape) { if(png.width % 64 === 0 && png.height % 32 === 0) { return [createHashInternal(png), stream] } else if (png.width % 22 === 0 && png.height % 17 === 0) { const newPNG = new PNG({ width: Math.ceil(png.width / 22), height: Math.ceil(png.height / 17), }) for (let y = 0; y < png.height; y++) { for (let x = 0; x < png.width; x++) { const idx = (png.width * y + x) << 2 newPNG.data[idx] = png.data[idx] newPNG.data[idx + 1] = png.data[idx + 1] newPNG.data[idx + 2] = png.data[idx + 2] newPNG.data[idx + 3] = png.data[idx + 3] } } for (let y = png.height; y < newPNG.height; y++) { for (let x = png.width; x < newPNG.width; x++) { const idx = (png.width * y + x) << 2 newPNG.data[idx] = 0 newPNG.data[idx + 1] = 0 newPNG.data[idx + 2] = 0 newPNG.data[idx + 3] = 0 } } return [createHashInternal(newPNG), newPNG.pack()] } } else { if(png.width % 64 === 0 && png.height % 32 === 0) { const newPNG = new PNG({ width: png.width, height: png.height }) for (let y = 0; y < png.height; y++) { for (let x = 0; x < png.width; x++) { const idx = (png.width * y + x) << 2 newPNG.data[idx] = png.data[idx] newPNG.data[idx + 1] = png.data[idx + 1] newPNG.data[idx + 2] = png.data[idx + 2] newPNG.data[idx + 3] = png.data[idx + 3] } } return [createHashInternal(png), newPNG.pack()] } } throw new Error('Invalid image size') }, sign: (data) => { return createSign('RSA-SHA1').update(data).sign({ key: createPrivateKey(server.keys.privateKey) }, 'hex') } } const createHashInternal = (png) => { const hash = createHash('sha256') for (let y = 0; y < png.height; y++) { for (let x = 0; x < png.width; x++) { const idx = (png.width * y + x) << 2 hash.update(png.data[idx].toString(16)) hash.update(png.data[idx + 1].toString(16)) hash.update(png.data[idx + 2].toString(16)) hash.update(png.data[idx + 3].toString(16)) } } return hash.digest('hex') }