完成皮肤数据的RSA签名和皮肤安全检查
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Qumolama.d
2022-05-13 10:16:32 +08:00
parent 324d8b3b54
commit b5111370a3
5 changed files with 104 additions and 6 deletions

View File

@ -1,6 +1,7 @@
import mongoose from 'mongoose'
const { Schema } = mongoose
import { uuidToNoSymboUUID } from '../generator.js'
import { ImageSecurity } from '../secure.js'
export const PlayerSchema = new Schema({
username: String, // 有符号 UUID
@ -89,13 +90,16 @@ export function getPlayerSerialization(player) {
}
}
const val = Buffer.from(JSON.stringify(textures)).toString('base64')
return {
uuid: uuidToNoSymboUUID(player.uuid),
name: player.username,
properties: [
{
name: "texturs",
value: Buffer.from(JSON.stringify(textures)).toString('base64')
value: val,
signature: ImageSecurity.sign(val),
}
]
}

View File

@ -1,3 +1,89 @@
export {
import { PNG } from 'pngjs'
import fs from 'fs'
import { createHash, createPrivateKey, createSign } from 'crypto'
import { config } from './config.js'
export const ImageSecurity = {
createImageHash: async (path, isCape) => {
const png = await new Promise((resolve, reject) => {
fs.createReadStream(path).pipe(new PNG()).on('metadata', function(metadata) {
if(metadata.width * metadata.height > 40960) {
reject('Image size exceeds 40960 pixels')
}
}).on('parsed', function(buff) {
resolve(this)
})
})
if (isCape) {
if(png.width % 64 === 0 && png.height % 32 === 0) {
return createHashInternal(png)
} 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)
}
} 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)
}
}
throw new Error('Invalid image size')
},
sign: async (data) => {
return createSign('RSA-SHA1').update(data).sign(createPrivateKey(config.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')
}